diff --git a/deltatech_stock_analytic/models/stock.py b/deltatech_stock_analytic/models/stock.py index 8d2723b137..a8a4722102 100644 --- a/deltatech_stock_analytic/models/stock.py +++ b/deltatech_stock_analytic/models/stock.py @@ -14,7 +14,7 @@ def write(self, values): for move in self: if not move.price_unit: if move.stock_valuation_layer_ids: - price_unit = move._get_price_unit() + price_unit = next(iter(move._get_price_unit().values())) else: price_unit = move.product_id.standard_price else: diff --git a/deltatech_stock_inventory/__manifest__.py b/deltatech_stock_inventory/__manifest__.py index 8b1881febf..c18ddf041e 100644 --- a/deltatech_stock_inventory/__manifest__.py +++ b/deltatech_stock_inventory/__manifest__.py @@ -28,6 +28,7 @@ # "wizard/stock_change_product_qty_view.xml", "views/product_kanban_view.xml", "wizard/stock_inventory_merge.xml", + "wizard/product_replenish_views.xml", ], "images": ["images/main_screenshot.png"], "installable": True, diff --git a/deltatech_stock_inventory/i18n/ro.po b/deltatech_stock_inventory/i18n/ro.po index ff516feb8d..dcd6750117 100644 --- a/deltatech_stock_inventory/i18n/ro.po +++ b/deltatech_stock_inventory/i18n/ro.po @@ -157,7 +157,7 @@ msgstr "" #. module: deltatech_stock_inventory #: model:res.groups,name:deltatech_stock_inventory.group_change_inventory_date msgid "Allow to change inventory date" -msgstr "" +msgstr "Permite schimbarea datei inventarului" #. module: deltatech_stock_inventory #: model:ir.model.fields,help:deltatech_stock_inventory.field_stock_inventory__prefill_counted_quantity @@ -169,7 +169,7 @@ msgstr "" #. module: deltatech_stock_inventory #: model:ir.model.fields,help:deltatech_stock_inventory.field_stock_inventory__start_empty msgid "Allows to start with an empty inventory." -msgstr "" +msgstr "Permite să începeți cu un inventar gol." #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__message_attachment_count @@ -184,11 +184,21 @@ msgid "" " suggested counted quantity." msgstr "" +#. module: deltatech_stock_inventory +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__can_archive_svl +msgid "Can Archive Svl" +msgstr "Poate arhiva Svl" + #. module: deltatech_stock_inventory #: model:res.groups,name:deltatech_stock_inventory.group_merge_inventory msgid "Can merge inventory documents" msgstr "Unire documente inventar" +#. module: deltatech_stock_inventory +#: model:res.groups,name:deltatech_stock_inventory.group_view_inventory_button +msgid "Can update quantities" +msgstr "Buton inventar în produs" + #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_stock_confirm_inventory_form #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_stock_inventory_merge_form @@ -198,12 +208,12 @@ msgstr "Anulează" #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_inventory_form msgid "Cancel Inventory" -msgstr "" +msgstr "Anulează inventar" #. module: deltatech_stock_inventory #: model:ir.model.fields.selection,name:deltatech_stock_inventory.selection__stock_inventory__state__cancel msgid "Cancelled" -msgstr "" +msgstr "Anulat" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_product_product__loc_case @@ -215,6 +225,12 @@ msgstr "" msgid "Case" msgstr "Cutie" +#. module: deltatech_stock_inventory +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_product_warehouse_location__loc_case +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory_line__loc_case +msgid "Case Name" +msgstr "Cutie" + #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory_line__categ_id msgid "Category" @@ -223,7 +239,12 @@ msgstr "Categorie" #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_stock_confirm_inventory_form msgid "Change" -msgstr "" +msgstr "Schimbă" + +#. module: deltatech_stock_inventory +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__archive_svl +msgid "Clear old valuation" +msgstr "Șterge evaluările vechi" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__company_id @@ -240,7 +261,7 @@ msgstr "Confirmă" #. module: deltatech_stock_inventory #: model:ir.actions.act_window,name:deltatech_stock_inventory.action_stock_confirm_inventory msgid "Confirm Inventory" -msgstr "" +msgstr "Confirmă inventar" #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_product_template_warehouse_form @@ -299,12 +320,12 @@ msgstr "Data" #. module: deltatech_stock_inventory #: model:ir.model.fields.selection,name:deltatech_stock_inventory.selection__stock_inventory__prefill_counted_quantity__counted msgid "Default to stock on hand" -msgstr "" +msgstr "Implicit stocul disponibil" #. module: deltatech_stock_inventory #: model:ir.model.fields.selection,name:deltatech_stock_inventory.selection__stock_inventory__prefill_counted_quantity__zero msgid "Default to zero" -msgstr "" +msgstr "Implicit zero" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory_line__difference_qty @@ -314,7 +335,7 @@ msgstr "Diferență" #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.stock_inventory_line_search msgid "Difference different than zero" -msgstr "" +msgstr "Diferență diferită de zero" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_product_warehouse_location__display_name @@ -339,7 +360,7 @@ msgstr "Golește inventar" #. module: deltatech_stock_inventory #: model:ir.model.fields,help:deltatech_stock_inventory.field_stock_inventory_line__product_tracking msgid "Ensure the traceability of a storable product in your warehouse." -msgstr "" +msgstr "Asigurați trasabilitatea unui produs stocabil în depozitul dvs." #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__message_follower_ids @@ -428,7 +449,7 @@ msgstr "" #: model:ir.model.fields.selection,name:deltatech_stock_inventory.selection__stock_inventory__state__confirm #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_inventory_filter msgid "In Progress" -msgstr "" +msgstr "În desfășurare" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__exhausted @@ -478,7 +499,7 @@ msgstr "" #. module: deltatech_stock_inventory #: model:ir.model,name:deltatech_stock_inventory.model_stock_inventory_adjustment_name msgid "Inventory Adjustment Reference / Reason" -msgstr "" +msgstr "Referință / Motiv ajustare inventar" #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory__date @@ -520,6 +541,11 @@ msgstr "" msgid "Inventory Position" msgstr "Poziții inventar" +#. module: deltatech_stock_inventory +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory_adjustment_name__inventory_adjustment_name +msgid "Inventory Reason" +msgstr "Motiv inventar" + #. module: deltatech_stock_inventory #: model:ir.model.fields,field_description:deltatech_stock_inventory.field_stock_inventory_line__is_editable msgid "Is Editable" @@ -787,6 +813,11 @@ msgstr "" msgid "Product Unit of Measure" msgstr "" +#. module: deltatech_stock_inventory +#: model:ir.model,name:deltatech_stock_inventory.model_product_product +msgid "Product Variant" +msgstr "Variantă produs" + #. module: deltatech_stock_inventory #: model:ir.model,name:deltatech_stock_inventory.model_product_warehouse_location msgid "Product Warehouse Location" @@ -903,6 +934,11 @@ msgstr "" msgid "Shelf" msgstr "Poliță" +#. module: deltatech_stock_inventory +#: model:ir.model.fields,field_description:deltatech_stock_inventory.field_product_warehouse_location__loc_shelf +msgid "Shelf Name" +msgstr "Poliță" + #. module: deltatech_stock_inventory #: model_terms:ir.ui.view,arch_db:deltatech_stock_inventory.view_inventory_form msgid "Show Lines" diff --git a/deltatech_stock_inventory/models/__init__.py b/deltatech_stock_inventory/models/__init__.py index 3676f8e4c0..e75ee46255 100644 --- a/deltatech_stock_inventory/models/__init__.py +++ b/deltatech_stock_inventory/models/__init__.py @@ -8,3 +8,5 @@ from . import product from . import stock_move from . import stock_quant +from . import stock_picking +from . import stock_valuation_layer diff --git a/deltatech_stock_inventory/models/product.py b/deltatech_stock_inventory/models/product.py index 3660e3f221..f83e61b3d4 100644 --- a/deltatech_stock_inventory/models/product.py +++ b/deltatech_stock_inventory/models/product.py @@ -3,7 +3,8 @@ # See README.rst file on addons root folder for license details -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import UserError class ProductWarehouseLocation(models.Model): @@ -12,10 +13,10 @@ class ProductWarehouseLocation(models.Model): product_id = fields.Many2one("product.template", index=True) warehouse_id = fields.Many2one("stock.warehouse", index=True) - loc_rack = fields.Char("Rack", size=16) - loc_row = fields.Char("Row", size=16) - loc_shelf = fields.Char("Shelf", size=16) - loc_case = fields.Char("Case", size=16) + loc_rack = fields.Char("Rack Name", size=16) + loc_row = fields.Char("Row Name", size=16) + loc_shelf = fields.Char("Shelf Name", size=16) + loc_case = fields.Char("Case Name", size=16) _sql_constraints = [ ( @@ -35,7 +36,7 @@ class ProductTemplate(models.Model): loc_case = fields.Char("Case", size=16, compute="_compute_loc", inverse="_inverse_loc") warehouse_loc_ids = fields.One2many("product.warehouse.location", "product_id") - # is_inventory_ok = fields.Boolean("Inventory OK", tracking=True) + is_inventory_ok = fields.Boolean("Inventory OK", tracking=True) # nu are senes daca sunt mai multe locatii warehouse_stock = fields.Text(string="Stock/WH", compute="_compute_warehouse_stocks") def _compute_warehouse_stocks(self): @@ -61,8 +62,11 @@ def _compute_loc(self): location_id = self.env.context.get("location", False) if not warehouse_id and location_id: if isinstance(location_id, int): - location = self.env["stock.location"].browse(location_id) - warehouse_id = location.warehouse_id.id + # location = self.env["stock.location"].browse(location_id) + # warehouse_id = location.warehouse_id.id + warehouse = self.env["stock.warehouse"].search([("lot_stock_id", "=", location_id)], limit=1) + if warehouse: + warehouse_id = warehouse.id if not warehouse_id: warehouse_id = self.env.ref("stock.warehouse0").id @@ -100,11 +104,156 @@ def _inverse_loc(self): else: self.env["product.warehouse.location"].sudo().create(values) + # def write(self, vals): + # res = super().write(vals) + # if "is_inventory_ok" in vals: + # self.with_context(active_test=False).mapped("product_variant_ids").write( + # {"is_inventory_ok": vals.get("is_inventory_ok")} + # ) + # return res + + def variants_is_ok(self): + self.ensure_one() + is_inventory_ok = True + for product in self.product_variant_ids: + if not product.is_inventory_ok: + is_inventory_ok = False + return is_inventory_ok + + def get_location(self): + """ + Get the location (first location from product, other are ignored) + :return: stock location if found, else False + """ + self.ensure_one() + if self.warehouse_loc_ids: + warehouse_loc = self.warehouse_loc_ids[0] + if warehouse_loc.loc_row and warehouse_loc.loc_rack: + if "/" in warehouse_loc.loc_row: + # multiple locations pe warehouse + rows = warehouse_loc.loc_row.split("/") + racks = warehouse_loc.loc_rack.split("/") + rack = racks[0] + row = rows[0] + else: + rack = warehouse_loc.loc_rack + row = warehouse_loc.loc_row + + # search for location + location_dest = ( + warehouse_loc.warehouse_id.code + + "/" + + warehouse_loc.warehouse_id.lot_stock_id.name + + "/" + + row + + "/" + + rack + ) + locations = self.env["stock.location"].search([("complete_name", "=", location_dest)]) + if not locations: + # try without leading zeros + if rack[0] == "0": + rack = rack[1:] + location_dest = ( + warehouse_loc.warehouse_id.code + + "/" + + warehouse_loc.warehouse_id.lot_stock_id.name + + "/" + + row + + "/" + + rack + ) + locations = self.env["stock.location"].search([("complete_name", "=", location_dest)]) + if not locations: + return False + else: + return locations[0] + else: + return False + else: + return False + + def create_putaway_rule(self): + """ + Create a putaway rule, if it doesn't exist + :return: None + """ + vals = [] + for product in self: + location_dest = product.get_location() + if location_dest: + location_source = location_dest.warehouse_id.lot_stock_id + for product_variant in product.product_variant_ids: + if not product_variant.putaway_rule_ids.filtered( + lambda loc_in: loc_in.location_in_id == location_source + ): + value = { + "company_id": product.company_id or self.env.user.company_id.id, + "product_id": product_variant.id, + "location_in_id": location_source.id, + "location_out_id": location_dest.id, + } + vals.append(value) + # else: + # raise UserError( + # _(f"No location can be fount for product {product.name}. Check product stock configuration") + # ) + if vals: + self.env["stock.putaway.rule"].create(vals) + + def move_to_putaway_location(self): + """ + Creates a picking to move all product variants in location found in variant's putaway rules + No tracking (lots or serials) is used + :return: created picking + """ + self.ensure_one() + location_id = False + location_dest_id = False + values = [] + for product in self.product_variant_ids: + if product.putaway_rule_ids: + rule_id = product.putaway_rule_ids[0] + location_id = rule_id.location_in_id + location_dest_id = rule_id.location_out_id + quants = self.env["stock.quant"]._gather(product, location_id) + qty = sum(quants.mapped("quantity")) + value = { + "company_id": self.company_id or self.env.user.company_id.id, + "date": fields.Datetime.now(), + "product_id": product.id, + "name": product.display_name, + "location_id": location_id.id, + "location_dest_id": location_dest_id.id, + "product_uom": product.uom_id.id, + "product_uom_qty": qty, + "picked": True, + } + values.append(value) + else: + raise UserError(_(f"No putaway rule found for {product.name}")) + if values: + picking_type = self.env.ref("stock.picking_type_internal") + picking_values = { + "picking_type_id": picking_type.id, + "location_id": location_id.id, + "location_dest_id": location_dest_id.id, + "move_ids_without_package": [(0, 0, line_vals) for line_vals in values], + } + picking = self.env["stock.picking"].create(picking_values) + picking.action_confirm() + for move in picking.move_ids: + move._set_quantity_done(move.product_uom_qty) + + picking.move_ids.picked = True + picking._action_done() + return picking + class ProductProduct(models.Model): _inherit = "product.product" - # is_inventory_ok = fields.Boolean("Inventory OK") + is_inventory_ok = fields.Boolean("Inventory OK") @api.model def get_theoretical_quantity( diff --git a/deltatech_stock_inventory/models/stock.py b/deltatech_stock_inventory/models/stock.py index 1c60de5b4f..19cf622751 100644 --- a/deltatech_stock_inventory/models/stock.py +++ b/deltatech_stock_inventory/models/stock.py @@ -69,9 +69,10 @@ class StockInventoryLine(models.Model): categ_id = fields.Many2one("product.category", string="Category", related="product_id.categ_id", store=True) standard_price = fields.Float(string="Price") - loc_rack = fields.Char("Rack", size=16, compute="_compute_loc", store=True) - loc_row = fields.Char("Row", size=16, compute="_compute_loc", store=True) - loc_case = fields.Char("Case", size=16, compute="_compute_loc", store=True) + + loc_rack = fields.Char("Rack Name", size=16, compute="_compute_loc", store=True) + loc_row = fields.Char("Row Name", size=16, compute="_compute_loc", store=True) + loc_case = fields.Char("Case Name", size=16, compute="_compute_loc", store=True) is_ok = fields.Boolean("Is Ok", default=True) @api.depends("location_id", "product_id") @@ -87,8 +88,12 @@ def _compute_loc(self): def create(self, vals_list): for values in vals_list: if "standard_price" not in values: - product = self.env["product.product"].browse(values["product_id"]) - values["standard_price"] = product.standard_price + if "product_id" in values: + product = self.env["product.product"].browse(values["product_id"]) + values["standard_price"] = product.standard_price + elif self.env.context.get("default_product_id", False): + product = self.env["product.product"].browse(self.env.context.get("default_product_id", False)) + values["standard_price"] = product.standard_price return super().create(vals_list) @api.onchange( @@ -124,10 +129,13 @@ def _generate_moves(self): for inventory_line in self: if ( not inventory_line.theoretical_qty - or inventory_line.product_id.cost_method == "fifo" + or ( + inventory_line.product_id.cost_method == "fifo" + or inventory_line.product_id.cost_method == "average" + ) and use_inventory_price ): - inventory_line.product_id.with_context(disable_auto_svl=True).write( + inventory_line.product_id.sudo().with_context(disable_auto_svl=True).write( {"standard_price": inventory_line.standard_price} ) moves = super()._generate_moves() diff --git a/deltatech_stock_inventory/models/stock_inventory.py b/deltatech_stock_inventory/models/stock_inventory.py index 9764b19d8d..48ed2eb272 100644 --- a/deltatech_stock_inventory/models/stock_inventory.py +++ b/deltatech_stock_inventory/models/stock_inventory.py @@ -5,6 +5,7 @@ from odoo.osv import expression from odoo.tools import float_compare, float_is_zero from odoo.tools.misc import OrderedSet +from odoo.tools.safe_eval import safe_eval from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG @@ -22,7 +23,6 @@ class Inventory(models.Model): ) date = fields.Datetime( "Inventory Date", - readonly=True, required=True, default=fields.Datetime.now, help="If the inventory adjustment is not validated, " @@ -85,6 +85,12 @@ class Inventory(models.Model): "Include Exhausted Products", help="Include also products with quantity of 0", ) + can_archive_svl = fields.Boolean(compute="_compute_archive_svl") + archive_svl = fields.Boolean(string="Clear old valuation") + + def _compute_archive_svl(self): + get_param = self.env["ir.config_parameter"].sudo().get_param + self.can_archive_svl = bool(safe_eval(get_param("inventory.can_archive_svl", "False"))) @api.onchange("company_id") def _onchange_company_id(self): @@ -177,7 +183,7 @@ def _action_done(self): % {"product_name": negative.product_id.display_name, "product_qty": negative.product_qty} ) self.action_check() - self.write({"state": "done", "date": fields.Datetime.now()}) + self.write({"state": "done", "date": self.date}) self.post_inventory() return True @@ -188,7 +194,36 @@ def post_inventory(self): # This is a normal behavior # as quants cannot be reuse from inventory location (users can still manually move the products before/after # the inventory if they want). - self.mapped("move_ids").filtered(lambda move: move.state != "done")._action_done() + + # archive old SVL's and write new svl if checked + # TODO: test and correct with storage stock sheet + if self.archive_svl: + # archive old svls + products = self.line_ids.product_id.ids + svls = self.env["stock.valuation.layer"].search([("product_id", "in", products)]) + svls.write({"active": False}) + # check if l10n_ro_stock_account installed + is_l10n_ro = False + svl_model = self.env["stock.valuation.layer"] + if hasattr(svl_model, "l10n_ro_account_id"): + is_l10n_ro = True + for line in self.line_ids: + if not is_l10n_ro: + move = line.create_inventory_in_move() + line.create_inventory_in_svl(move) + else: + old_svl_val, old_svl_qty = line.get_old_svl_value() + move_out = line.create_inventory_out_move(old_svl_qty) + line.create_inventory_out_svl(move_out, old_svl_val) + move_in = line.create_inventory_in_move() + line.with_context(is_l10n_ro=True).create_inventory_in_svl(move_in) + + move_ids = self.mapped("move_ids").filtered(lambda move: move.state != "done") + move_ids.picked = True + move_ids._action_done() + move_ids = self.mapped("move_ids").filtered(lambda move: move.state != "done") + if move_ids: + raise UserError(_("Some products have not been moved. Please check the inventory moves.")) return True def action_check(self): @@ -453,13 +488,11 @@ def _domain_product_id(self): return "[('type', '=', 'product'), '|', ('company_id', '=', False), ('company_id', '=', company_id)]" is_editable = fields.Boolean(help="Technical field to restrict editing.") - inventory_id = fields.Many2one( - "stock.inventory", - "Inventory", - check_company=True, - index=True, - ondelete="cascade", + is_price_editable = fields.Boolean( + compute="_compute_is_price_editable", help="Technical field to restrict price editing." ) + inventory_id = fields.Many2one("stock.inventory", "Inventory", check_company=True, index=True, ondelete="cascade") + partner_id = fields.Many2one("res.partner", "Owner", check_company=True) product_id = fields.Many2one( "product.product", @@ -569,6 +602,23 @@ def _compute_outdated(self): else: line.outdated = False + def _compute_is_editable(self): + for line in self: + if line.inventory_id.state != "confirm": + line.is_editable = False + else: + line.is_editable = True + + def _compute_is_price_editable(self): + config_parameter = self.env["ir.config_parameter"].sudo() + use_inventory_price = config_parameter.get_param(key="stock.use_inventory_price", default="True") + use_inventory_price = safe_eval(use_inventory_price) + for line in self: + if not use_inventory_price: + line.is_price_editable = False + else: + line.is_price_editable = True + @api.onchange( "product_id", "location_id", @@ -642,7 +692,12 @@ def create(self, vals_list): ) values["theoretical_qty"] = theoretical_qty if "product_id" in values and "product_uom_id" not in values: - values["product_uom_id"] = product.product_tmpl_id.uom_id.id + if product: + values["product_uom_id"] = product.product_tmpl_id.uom_id.id + else: + if self.env.context.get("default_product_id", False): + product = self.env["product.product"].browse(self.env.context.get("default_product_id", False)) + values["product_uom_id"] = product.product_tmpl_id.uom_id.id res = super().create(vals_list) res._check_no_duplicate_line() return res @@ -746,6 +801,7 @@ def _get_move_values(self, qty, location_id, location_dest_id, out): "location_id": location_id, "location_dest_id": location_dest_id, "owner_id": self.partner_id.id, + "picked": True, }, ) ], @@ -856,3 +912,127 @@ def _search_outdated(self, operator, value): lines = self.search([("inventory_id", "=", self.env.context.get("default_inventory_id"))]) line_ids = lines.filtered(lambda line: line.outdated == value).ids return [("id", "in", line_ids)] + + # archive svl functions + def get_old_svl_value(self): + """ + Get existing SVLs value, qty + :return: old value, old quantity + """ + domain = [("product_id", "=", self.product_id.id), ("l10n_ro_location_dest_id", "=", self.location_id.id)] + in_svls = self.env["stock.valuation.layer"].with_context(active_test=False).search(domain) + in_svls_value = sum(in_svls.mapped("value")) + in_svls_quantity = sum(in_svls.mapped("quantity")) + domain = [("product_id", "=", self.product_id.id), ("l10n_ro_location_id", "=", self.location_id.id)] + out_svls = self.env["stock.valuation.layer"].with_context(active_test=False).search(domain) + out_svls_value = sum(out_svls.mapped("value")) + out_svls_quantity = sum(out_svls.mapped("quantity")) + return in_svls_value - out_svls_value, in_svls_quantity - out_svls_quantity + + def create_inventory_out_move(self, svl_qty): + """ + Creates a move from line location to inventory location + :param svl_qty: quantity to move + :return: created move + """ + # svl_total_value, svl_total_quantity = self.get_old_svl_value() + values = { + "company_id": self.company_id.id, + "date": self.inventory_id.date, + "location_dest_id": self.product_id.property_stock_inventory.id, + "location_id": self.location_id.id, + "name": "dummy_move_" + self.product_id.name, + "procure_method": "make_to_stock", + "product_id": self.product_id.id, + "product_uom": self.product_id.uom_id.id, + "product_uom_qty": self.theoretical_qty, + "state": "done", + } + move = self.env["stock.move"].create(values) + return move + + def create_inventory_out_svl(self, move_id, svl_value): + """ + Creates a svl for the inventory move + :param move_id: move to link to svl + :param svl_value: total value to move + :return: created svls + """ + if move_id.product_uom_qty: + unit_cost = svl_value / move_id.product_uom_qty + else: + unit_cost = 0 + svl_vals = { + "active": False, + "company_id": self.company_id.id, + "currency_id": self.company_id.currency_id.id, + "product_id": self.product_id.id, + "stock_move_id": move_id.id, + # "quantity": -1 * move_id.product_uom_qty, + "quantity": -1 * self.theoretical_qty, + "unit_cost": unit_cost, + "value": -1 * svl_value, + "remaining_value": 0, + "remaining_qty": 0, + "description": self.product_id.name + " -fix value", + } + if self.env.context.get("is_l10n_ro", False): + accounts = self.product_id.product_tmpl_id._get_product_accounts() + svl_vals["l10n_ro_account_id"] = accounts["stock_valuation"].id + svl_vals["l10n_ro_valued_type"] = "internal_transfer" + if self.prod_lot_id: + svl_vals["l10n_ro_lot_ids"] = [(4, self.prod_lot_id.id)] + svl = self.env["stock.valuation.layer"].create(svl_vals) + return svl + + def create_inventory_in_move(self): + """ + Creates a move from inventory location to line location. Theoretical quantity is used, because a new move + will be created by the inventory line (if quantities are different) + :return: created move + """ + # svl_total_value, svl_total_quantity = self.get_old_svl_value() + values = { + "company_id": self.company_id.id, + "date": self.inventory_id.date, + "location_dest_id": self.location_id.id, + "location_id": self.product_id.property_stock_inventory.id, + "name": "dummy_move_" + self.product_id.name, + "procure_method": "make_to_stock", + "product_id": self.product_id.id, + "product_uom": self.product_id.uom_id.id, + "product_uom_qty": self.theoretical_qty, + "state": "done", + } + move = self.env["stock.move"].create(values) + return move + + def create_inventory_in_svl(self, move_id): + """ + Creates a svl for the inventory move. Theoretical quantity is used, because a new svl will be created + by the inventory line's move for the difference (if exists) + :param move_id: move to link to svl + :return: created svls + """ + svl_vals = { + "active": True, + "company_id": self.company_id.id, + "currency_id": self.company_id.currency_id.id, + "product_id": self.product_id.id, + "stock_move_id": move_id.id, + "quantity": self.theoretical_qty, + "unit_cost": self.standard_price, + "value": self.theoretical_qty * self.standard_price, + "remaining_value": self.product_qty * self.standard_price, + "remaining_qty": self.theoretical_qty, + "description": self.product_id.name + " -fix value", + } + if self.env.context.get("is_l10n_ro", False): + accounts = self.product_id.product_tmpl_id._get_product_accounts() + svl_vals["l10n_ro_account_id"] = accounts["stock_valuation"].id + # svl_vals["l10n_ro_valued_type"] = "vasile" + # svl_vals["l10n_ro_valued_type"] = "inventory_return" + if self.prod_lot_id: + svl_vals["l10n_ro_lot_ids"] = [(4, self.prod_lot_id.id)] + svl = self.env["stock.valuation.layer"].create(svl_vals) + return svl diff --git a/deltatech_stock_inventory/models/stock_picking.py b/deltatech_stock_inventory/models/stock_picking.py new file mode 100644 index 0000000000..2c5cc7b3b0 --- /dev/null +++ b/deltatech_stock_inventory/models/stock_picking.py @@ -0,0 +1,13 @@ +# © 2015-2022 Deltatech +# Dorin Hongu 4 - - Manage Multiple Stock Locations - - - - - Manage Multiple Warehouses - - - - - User - - - - - Administrator - - - - - - - Manage Lots / Serial Numbers - - Display Serial & Lot Number in Delivery Slips - - Manage Packages - - - - - Manage Push and Pull inventory flows - - - - - Manage Different Stock Owners - - A warning can be set on a partner (Stock) @@ -72,59 +32,7 @@ - - stock_picking multi-company - - [('company_id', 'in', company_ids)] - - - - Stock Operation Type multi-company - - [('company_id','in', company_ids)] - - - Stock Operation Type multi-company - - [('company_id','in', company_ids)] - - - - Stock Production Lot multi-company - - [('company_id','in', company_ids)] - - - - Warehouse multi-company - - [('company_id', 'in', company_ids)] - - - - Location multi-company - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - - - stock_move multi-company - - [('company_id', 'in', company_ids)] - - - - stock_move_line multi-company - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - - - stock_quant multi-company - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - Inventory Line multi-company @@ -138,17 +46,6 @@ [('company_id', 'in', company_ids)] - - stock_warehouse.orderpoint multi-company - - [('company_id', 'in', company_ids)] - - - - product_pulled_flow multi-company - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - stock_location_route multi-company @@ -156,23 +53,6 @@ ['|',('company_id','=',False),('company_id', 'in', company_ids)] - - stock_quant_package multi-company - - ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] - - - - stock_scrap_company multi-company - - [('company_id', 'in', company_ids)] - - - - report_stock_quantity_flow multi-company - - ['|',('company_id','=',False),('company_id', 'in', company_ids)] - diff --git a/deltatech_stock_inventory/views/product_view.xml b/deltatech_stock_inventory/views/product_view.xml index 16a65faf68..6fbeb1b285 100644 --- a/deltatech_stock_inventory/views/product_view.xml +++ b/deltatech_stock_inventory/views/product_view.xml @@ -21,7 +21,7 @@ groups="stock.group_stock_multi_warehouses" invisible="context.get('warehouse')" > - + diff --git a/deltatech_stock_inventory/views/stock_inventory_views.xml b/deltatech_stock_inventory/views/stock_inventory_views.xml index 52f6a308a0..20ec441c1e 100644 --- a/deltatech_stock_inventory/views/stock_inventory_views.xml +++ b/deltatech_stock_inventory/views/stock_inventory_views.xml @@ -16,6 +16,7 @@ + @@ -325,7 +326,7 @@ - + @@ -384,6 +385,25 @@ + + Move product to location + + + code + +records.create_putaway_rule() +picking = records.move_to_putaway_location() +if picking: + action = { + 'type': 'ir.actions.act_window', + 'res_id': picking.id, + 'res_model': 'stock.picking', + 'target': 'self', + "view_mode": "form", + } + + + + + + Replenish + product.replenish + + + + + + + + diff --git a/deltatech_stock_inventory/wizard/stock_confirm_inventory.py b/deltatech_stock_inventory/wizard/stock_confirm_inventory.py index 03bc8993e9..f27fea1e4c 100644 --- a/deltatech_stock_inventory/wizard/stock_confirm_inventory.py +++ b/deltatech_stock_inventory/wizard/stock_confirm_inventory.py @@ -9,53 +9,47 @@ class StockConfirmInventory(models.TransientModel): _name = "stock.confirm.inventory" _description = "Stock Confirm Inventory" + location_id = fields.Many2one("stock.location", string="Location", domain="[('usage', '=', 'internal')]") product_tmpl_id = fields.Many2one("product.template") - qty_available = fields.Float(related="product_tmpl_id.qty_available") - last_inventory_date = fields.Date( - string="Last Inventory Date", - compute="_compute_last_inventory", - readonly=True, - ) - last_inventory_id = fields.Many2one( - "stock.inventory", - string="Last Inventory", - compute="_compute_last_inventory", - readonly=True, - ) + qty_available = fields.Float(compute="_compute_last_inventory") + last_inventory_date = fields.Date(string="Last Inventory Date", compute="_compute_last_inventory") + last_inventory_id = fields.Many2one("stock.inventory", string="Last Inventory", compute="_compute_last_inventory") @api.model def default_get(self, fields_list): defaults = super().default_get(fields_list) defaults["product_tmpl_id"] = self.env.context.get("active_id", False) + active_model = self.env.context.get("active_model", False) + if active_model and active_model == "product.product": + product = self.env["product.product"].browse(defaults["product_tmpl_id"]) + defaults["product_tmpl_id"] = product.product_tmpl_id.id return defaults - @api.depends("product_tmpl_id") + @api.onchange("product_tmpl_id", "location_id") + def _onchange_product_tmpl_id(self): + self.last_inventory_date = False + self.last_inventory_id = False + self.qty_available = 0 + self._compute_last_inventory() + + @api.depends("product_tmpl_id", "location_id") def _compute_last_inventory(self): - for product in self.product_tmpl_id.product_variant_ids: - domain = [("product_id", "=", product.id), ("is_ok", "=", True)] - line = self.env["stock.inventory.line"].search(domain, limit=1, order="id desc") - self.last_inventory_id = line.inventory_id - self.last_inventory_date = line.inventory_id.date + for rec in self: + for product in rec.product_tmpl_id.product_variant_ids: + domain = [ + ("product_id", "=", product.id), + ("is_ok", "=", True), + ("location_id", "=", rec.location_id.id), + ] + line = self.env["stock.inventory.line"].search(domain, limit=1, order="id desc") + rec.last_inventory_id = line.inventory_id + rec.last_inventory_date = line.inventory_id.date + rec.qty_available = product.with_context(location=rec.location_id.id).qty_available def confirm_actual_inventory(self): products = self.product_tmpl_id.product_variant_ids - inventory_values = {"state": "confirm", "line_ids": []} - quants = self.env["stock.quant"].search([("product_id", "in", products.ids)]) - for quant in quants: - if quant.location_id.usage == "internal" and ( - not quant.last_inventory_date - or (quant.last_inventory_date and quant.last_inventory_date < fields.Date.today()) - ): - values = { - "product_id": quant.product_id.id, - "product_uom_id": quant.product_id.uom_id.id, - "location_id": quant.location_id.id, - "theoretical_qty": quant.quantity, - "product_qty": quant.quantity, - "standard_price": quant.product_id.product_tmpl_id.standard_price, - "is_ok": True, - } - inventory_values["line_ids"].append((0, 0, values)) - if inventory_values["line_ids"]: - inventory = self.env["stock.inventory"].create(inventory_values) - inventory.action_validate() + + quants = self.env["stock.quant"].search( + [("product_id", "in", products.ids), ("location_id", "=", self.location_id.id)] + ) + quants.action_confirm_inventory() diff --git a/deltatech_stock_inventory/wizard/stock_confirm_inventory_view.xml b/deltatech_stock_inventory/wizard/stock_confirm_inventory_view.xml index a0000a4603..74afbd85bf 100644 --- a/deltatech_stock_inventory/wizard/stock_confirm_inventory_view.xml +++ b/deltatech_stock_inventory/wizard/stock_confirm_inventory_view.xml @@ -9,6 +9,7 @@ + diff --git a/deltatech_stock_valuation/models/stock_move.py b/deltatech_stock_valuation/models/stock_move.py index d038f3e0cc..0253ee93ef 100644 --- a/deltatech_stock_valuation/models/stock_move.py +++ b/deltatech_stock_valuation/models/stock_move.py @@ -33,6 +33,4 @@ def _prepare_account_move_line(self, qty, cost, credit_account_id, debit_account line[2].update({"valuation_area_id": valuation_area.id}) return res - # pretul de iesire din stoc se va calula in functie de aria de evaluare - def _get_price_unit(self): - return super()._get_price_unit() +