diff --git a/delivery_stamps/models/delivery_stamps.py b/delivery_stamps/models/delivery_stamps.py index 8f346f55..6a489fa4 100644 --- a/delivery_stamps/models/delivery_stamps.py +++ b/delivery_stamps/models/delivery_stamps.py @@ -109,36 +109,17 @@ class ProviderStamps(models.Model): ret_val = service.create_shipping() ret_val.ShipDate = date_planned.split()[0] if date_planned else date.today().isoformat() - ret_val.FromZIPCode = order.warehouse_id.partner_id.zip + ret_val.FromZIPCode = self.get_shipper_warehouse(order=order).zip ret_val.ToZIPCode = order.partner_shipping_id.zip ret_val.PackageType = self._stamps_package_type() ret_val.ServiceType = self.stamps_service_type ret_val.WeightLb = weight return ret_val - def _get_order_for_picking(self, picking): - if picking.sale_id: - return picking.sale_id - return None - - def _get_company_for_order(self, order): - company = order.company_id - if order.team_id and order.team_id.subcompany_id: - company = order.team_id.subcompany_id.company_id - elif order.analytic_account_id and order.analytic_account_id.subcompany_id: - company = order.analytic_account_id.subcompany_id.company_id - return company - - def _get_company_for_picking(self, picking): - order = self._get_order_for_picking(picking) - if order: - return self._get_company_for_order(order) - return picking.company_id - def _stamps_get_addresses_for_picking(self, picking): - company = self._get_company_for_picking(picking) - from_ = picking.picking_type_id.warehouse_id.partner_id - to = picking.partner_id + company = self.get_shipper_company(picking=picking) + from_ = self.get_shipper_warehouse(picking=picking) + to = self.get_recipient(picking=picking) return company, from_, to def _stamps_get_shippings_for_picking(self, service, picking): @@ -251,7 +232,7 @@ class ProviderStamps(models.Model): company, from_partner, to_partner = self._stamps_get_addresses_for_picking(picking) from_address = service.create_address() - from_address.FullName = company.partner_id.name + from_address.FullName = company.name from_address.Address1 = from_partner.street if from_partner.street2: from_address.Address2 = from_partner.street2 diff --git a/external/hibou-oca/connector-magento b/external/hibou-oca/connector-magento index 8cce7538..802fe38d 160000 --- a/external/hibou-oca/connector-magento +++ b/external/hibou-oca/connector-magento @@ -1 +1 @@ -Subproject commit 8cce7538e6f81adc8411288430a350e98ef1349a +Subproject commit 802fe38dc14025410960072b881a68fcc00cc7bb diff --git a/product_catch_weight/models/account_invoice.py b/product_catch_weight/models/account_invoice.py index ee8c9f92..11879f5b 100644 --- a/product_catch_weight/models/account_invoice.py +++ b/product_catch_weight/models/account_invoice.py @@ -25,10 +25,14 @@ class AccountInvoiceLine(models.Model): move_lines = self.sale_line_ids.mapped('move_ids.move_line_ids') else: move_lines = self.purchase_line_id.mapped('move_ids.move_line_ids') - for move_line in move_lines: + for move_line in move_lines.filtered(lambda l: l.lot_id): qty_done = move_line.qty_done + current_qty_done = qty_done + qty_done_total r = move_line.lot_id.catch_weight_ratio - ratio = ((ratio * qty_done_total) + (qty_done * r)) / (qty_done + qty_done_total) + if current_qty_done == 0: + ratio = 0 + else: + ratio = ((ratio * qty_done_total) + (qty_done * r)) / current_qty_done qty_done_total += qty_done catch_weight += move_line.lot_id.catch_weight price = price * ratio diff --git a/product_catch_weight/models/stock.py b/product_catch_weight/models/stock.py index 1ad5d3b6..c5cc9363 100644 --- a/product_catch_weight/models/stock.py +++ b/product_catch_weight/models/stock.py @@ -8,7 +8,6 @@ class StockProductionLot(models.Model): catch_weight = fields.Float(string='Catch Weight', digits=(10, 4)) catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id') - @api.depends('catch_weight') def _compute_catch_weight_ratio(self): for lot in self: @@ -44,3 +43,14 @@ class StockMoveLine(models.Model): catch_weight_uom_id = fields.Many2one('product.uom', string='Catch Weight UOM') lot_catch_weight = fields.Float(related='lot_id.catch_weight') lot_catch_weight_uom_id = fields.Many2one('product.uom', related='product_id.catch_weight_uom_id') + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + has_catch_weight = fields.Boolean(string="Has Catch Weight", compute='_compute_has_catch_weight', store=True) + + @api.depends('move_lines.product_catch_weight_uom_id') + def _compute_has_catch_weight(self): + for picking in self: + picking.has_catch_weight = any(picking.mapped('move_lines.product_catch_weight_uom_id')) diff --git a/product_catch_weight/tests/test_catch_weight.py b/product_catch_weight/tests/test_catch_weight.py index 1b13cb55..48821d7a 100644 --- a/product_catch_weight/tests/test_catch_weight.py +++ b/product_catch_weight/tests/test_catch_weight.py @@ -30,6 +30,7 @@ class TestPicking(TransactionCase): 'uom_po_id': self.product_uom_id.id, 'catch_weight_uom_id': self.ref_uom_id.id, }) + self.pricelist = self.env.ref('product.list0') # def test_creation(self): @@ -69,6 +70,7 @@ class TestPicking(TransactionCase): 'partner_invoice_id': self.partner1.id, 'partner_shipping_id': self.partner1.id, 'order_line': [(0, 0, {'product_id': self.product1.id})], + 'pricelist_id': self.pricelist.id, }) so.action_confirm() self.assertTrue(so.state in ('sale', 'done')) @@ -104,6 +106,7 @@ class TestPicking(TransactionCase): 'partner_invoice_id': self.partner1.id, 'partner_shipping_id': self.partner1.id, 'order_line': [(0, 0, {'product_id': self.product1.id, 'product_uom_qty': 2.0})], + 'pricelist_id': self.pricelist.id, }) so.action_confirm() self.assertTrue(so.state in ('sale', 'done')) diff --git a/product_catch_weight/views/stock_views.xml b/product_catch_weight/views/stock_views.xml index 40e2d9dd..3ce9015d 100644 --- a/product_catch_weight/views/stock_views.xml +++ b/product_catch_weight/views/stock_views.xml @@ -13,16 +13,6 @@ - - - - - - - - - - stock.move.operations.form.inherit stock.move @@ -36,6 +26,7 @@ + stock.move.line.operations.tree.inherit stock.move.line @@ -49,6 +40,7 @@ + product.template.common.form.inherit product.template @@ -59,4 +51,18 @@ + + + stock.view.picking.internal.search.inherit + stock.picking + + + + + + + + + + \ No newline at end of file diff --git a/rma/wizard/rma_lines_views.xml b/rma/wizard/rma_lines_views.xml index 9515fd03..934c8f71 100644 --- a/rma/wizard/rma_lines_views.xml +++ b/rma/wizard/rma_lines_views.xml @@ -7,7 +7,7 @@ - + diff --git a/rma_sale/wizard/rma_lines_views.xml b/rma_sale/wizard/rma_lines_views.xml index e6486124..dd7a8e03 100644 --- a/rma_sale/wizard/rma_lines_views.xml +++ b/rma_sale/wizard/rma_lines_views.xml @@ -7,7 +7,7 @@ - + diff --git a/sale_line_change/__init__.py b/sale_line_change/__init__.py new file mode 100644 index 00000000..40272379 --- /dev/null +++ b/sale_line_change/__init__.py @@ -0,0 +1 @@ +from . import wizard diff --git a/sale_line_change/__manifest__.py b/sale_line_change/__manifest__.py new file mode 100644 index 00000000..02a5d98b --- /dev/null +++ b/sale_line_change/__manifest__.py @@ -0,0 +1,25 @@ +{ + 'name': 'Sale Line Change', + 'summary': 'Change Confirmed Sale Lines Routes or Warehouses.', + 'version': '11.0.1.0.0', + 'author': "Hibou Corp.", + 'category': 'Sale', + 'license': 'AGPL-3', + 'complexity': 'expert', + 'images': [], + 'website': "https://hibou.io", + 'description': """ +""", + 'depends': [ + 'sale_sourced_by_line', + 'sale_stock', + 'stock_dropshipping', + ], + 'demo': [], + 'data': [ + 'wizard/sale_line_change_views.xml', + 'views/sale_views.xml', + ], + 'auto_install': False, + 'installable': True, +} diff --git a/sale_line_change/tests/__init__.py b/sale_line_change/tests/__init__.py new file mode 100644 index 00000000..27bc6136 --- /dev/null +++ b/sale_line_change/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_line_change diff --git a/sale_line_change/tests/test_sale_line_change.py b/sale_line_change/tests/test_sale_line_change.py new file mode 100644 index 00000000..7409446b --- /dev/null +++ b/sale_line_change/tests/test_sale_line_change.py @@ -0,0 +1,99 @@ +from odoo.tests import common +from odoo.exceptions import ValidationError + + +class TestSaleLineChange(common.TransactionCase): + def setUp(self): + super(TestSaleLineChange, self).setUp() + self.warehouse0 = self.env.ref('stock.warehouse0') + self.warehouse1 = self.env['stock.warehouse'].create({ + 'company_id': self.env.user.company_id.id, + # 'partner_id': self.env.user.company_id.partner_id.id, + 'name': 'TWH1', + 'code': 'TWH1', + }) + self.product1 = self.env.ref('product.product_product_24') + self.partner1 = self.env.ref('base.res_partner_12') + self.so1 = self.env['sale.order'].create({ + 'partner_id': self.partner1.id, + 'partner_invoice_id': self.partner1.id, + 'partner_shipping_id': self.partner1.id, + 'order_line': [(0, 0, { + 'product_id': self.product1.id, + 'name': 'N/A', + 'product_uom_qty': 1.0, + 'price_unit': 100.0, + })] + }) + self.dropship_route = self.env.ref('stock_dropshipping.route_drop_shipping') + self.warehouse0_route = self.warehouse0.route_ids.filtered(lambda r: r.name.find('Ship') >= 0) + + def test_00_sale_change_warehouse(self): + so = self.so1 + + so.action_confirm() + self.assertTrue(so.state in ('sale', 'done')) + self.assertTrue(so.picking_ids) + org_picking = so.picking_ids + self.assertEqual(org_picking.picking_type_id.warehouse_id, self.warehouse0) + + wiz = self.env['sale.line.change.order'].with_context(default_order_id=so.id).create({}) + self.assertTrue(wiz.line_ids) + wiz.line_ids.line_warehouse_id = self.warehouse1 + wiz.line_ids.line_date_planned = '2018-01-01 00:00:00' + wiz.apply() + + self.assertTrue(len(so.picking_ids) == 2) + self.assertTrue(org_picking.state == 'cancel') + new_picking = so.picking_ids - org_picking + self.assertTrue(new_picking) + self.assertEqual(new_picking.picking_type_id.warehouse_id, self.warehouse1) + self.assertEqual(new_picking.scheduled_date, '2018-01-01 00:00:00') + + def test_01_sale_change_route(self): + so = self.so1 + + so.action_confirm() + self.assertTrue(so.state in ('sale', 'done')) + self.assertTrue(so.picking_ids) + org_picking = so.picking_ids + self.assertEqual(org_picking.picking_type_id.warehouse_id, self.warehouse0) + + # Change route on wizard line + wiz = self.env['sale.line.change.order'].with_context(default_order_id=so.id).create({}) + self.assertTrue(wiz.line_ids) + wiz.line_ids.line_route_id = self.dropship_route + wiz.apply() + + # Check that RFQ/PO was created. + self.assertTrue(org_picking.state == 'cancel') + po_line = self.env['purchase.order.line'].search([('sale_line_id', '=', so.order_line.id)]) + self.assertTrue(po_line) + + def test_02_sale_dropshipping_to_warehouse(self): + self.assertTrue(self.warehouse0_route) + self.product1.route_ids += self.dropship_route + so = self.so1 + + so.action_confirm() + self.assertTrue(so.state in ('sale', 'done')) + self.assertFalse(so.picking_ids) + + # Change route on wizard line + wiz = self.env['sale.line.change.order'].with_context(default_order_id=so.id).create({}) + self.assertTrue(wiz.line_ids) + wiz.line_ids.line_route_id = self.warehouse0_route + wiz.line_ids.line_date_planned = '2018-01-01 00:00:00' + + # Wizard cannot complete because of non-cancelled Purchase Order. + with self.assertRaises(ValidationError): + wiz.apply() + + po_line = self.env['purchase.order.line'].search([('sale_line_id', '=', so.order_line.id)]) + po_line.order_id.button_cancel() + wiz.apply() + + # Check parameters on new picking + self.assertTrue(so.picking_ids) + self.assertEqual(so.picking_ids.picking_type_id.warehouse_id, self.warehouse0) + self.assertEqual(so.picking_ids.scheduled_date, '2018-01-01 00:00:00') diff --git a/sale_line_change/views/sale_views.xml b/sale_line_change/views/sale_views.xml new file mode 100644 index 00000000..57dafe2c --- /dev/null +++ b/sale_line_change/views/sale_views.xml @@ -0,0 +1,19 @@ + + + + sale.order.form.inherit + sale.order + + + + + + + + \ No newline at end of file diff --git a/sale_line_change/wizard/__init__.py b/sale_line_change/wizard/__init__.py new file mode 100644 index 00000000..1307b5ec --- /dev/null +++ b/sale_line_change/wizard/__init__.py @@ -0,0 +1 @@ +from . import sale_line_change diff --git a/sale_line_change/wizard/sale_line_change.py b/sale_line_change/wizard/sale_line_change.py new file mode 100644 index 00000000..fa14d79c --- /dev/null +++ b/sale_line_change/wizard/sale_line_change.py @@ -0,0 +1,89 @@ +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class SaleLineChangeOrder(models.TransientModel): + _name = 'sale.line.change.order' + _description = 'Sale Line Change Order' + + order_id = fields.Many2one('sale.order', string='Sale Order') + line_ids = fields.One2many('sale.line.change.order.line', 'change_order_id', string='Change Lines') + + @api.model + def default_get(self, fields): + rec = super(SaleLineChangeOrder, self).default_get(fields) + if 'order_id' in rec: + order = self.env['sale.order'].browse(rec['order_id']) + if not order: + return rec + + line_model = self.env['sale.line.change.order.line'] + rec['line_ids'] = [(0, 0, line_model.values_from_so_line(l)) for l in order.order_line] + return rec + + @api.multi + def apply(self): + self.ensure_one() + self.line_ids.apply() + return True + + +class SaleLineChangeOrderLine(models.TransientModel): + _name = 'sale.line.change.order.line' + + change_order_id = fields.Many2one('sale.line.change.order') + sale_line_id = fields.Many2one('sale.order.line', string='Sale Line') + line_ordered_qty = fields.Float(string='Ordered Qty') + line_delivered_qty = fields.Float(string='Delivered Qty') + line_reserved_qty = fields.Float(string='Reserved Qty') + line_date_planned = fields.Datetime(string='Planned Date') + line_warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse') + line_route_id = fields.Many2one('stock.location.route', string='Route') + + def values_from_so_line(self, so_line): + move_ids = so_line.move_ids + reserved_qty = sum(move_ids.mapped('reserved_availability')) + return { + 'sale_line_id': so_line.id, + 'line_ordered_qty': so_line.product_uom_qty, + 'line_delivered_qty': so_line.qty_delivered, + 'line_reserved_qty': reserved_qty, + 'line_date_planned': so_line.date_planned, + 'line_warehouse_id': so_line.warehouse_id.id, + 'line_route_id': so_line.route_id.id, + } + + def _apply(self): + self._apply_clean_dropship() + self._apply_clean_existing_moves() + self._apply_new_values() + self._apply_procurement() + + def _apply_clean_dropship(self): + po_line_model = self.env['purchase.order.line'].sudo() + po_lines = po_line_model.search([('sale_line_id', 'in', self.mapped('sale_line_id.id'))]) + + if po_lines and po_lines.filtered(lambda l: l.order_id.state != 'cancel'): + names = ', '.join(po_lines.filtered(lambda l: l.order_id.state != 'cancel').mapped('order_id.name')) + raise ValidationError('One or more lines has existing non-cancelled Purchase Orders associated: ' + names) + + def _apply_clean_existing_moves(self): + moves = self.mapped('sale_line_id.move_ids').filtered(lambda m: m.state != 'done') + moves._action_cancel() + + def _apply_new_values(self): + for line in self: + line.sale_line_id.write({ + 'date_planned': line.line_date_planned, + 'warehouse_id': line.line_warehouse_id.id, + 'route_id': line.line_route_id.id, + }) + + def _apply_procurement(self): + self.mapped('sale_line_id')._action_launch_procurement_rule() + + def apply(self): + changed_lines = self.filtered(lambda l: ( + l.sale_line_id.warehouse_id != l.line_warehouse_id + or l.sale_line_id.route_id != l.line_route_id)) + changed_lines._apply() diff --git a/sale_line_change/wizard/sale_line_change_views.xml b/sale_line_change/wizard/sale_line_change_views.xml new file mode 100644 index 00000000..bca33ca4 --- /dev/null +++ b/sale_line_change/wizard/sale_line_change_views.xml @@ -0,0 +1,40 @@ + + + + sale.line.change.order.form + sale.line.change.order + form + + + Changing Date Planned alone should be done on any existing Pickings or POs. + + + + + + + + + + + + + + + + + + Sale Line Change Order + sale.line.change.order + form + form + + new + + \ No newline at end of file diff --git a/sale_planner/models/partner.py b/sale_planner/models/partner.py index a0790ce7..a14c45d3 100644 --- a/sale_planner/models/partner.py +++ b/sale_planner/models/partner.py @@ -15,8 +15,8 @@ class Partner(models.Model): for partner in self.with_context(lang='en_US'): if ZipcodeSearchEngine and partner.zip: with ZipcodeSearchEngine() as search: - zipcode = search.by_zipcode(partner.zip) - if zipcode: + zipcode = search.by_zipcode(str(self.zip).split('-')[0]) + if zipcode and zipcode['Latitude']: partner.write({ 'partner_latitude': zipcode['Latitude'], 'partner_longitude': zipcode['Longitude'], diff --git a/sale_planner/wizard/order_planner.py b/sale_planner/wizard/order_planner.py index 8bea53b3..10ac7aba 100644 --- a/sale_planner/wizard/order_planner.py +++ b/sale_planner/wizard/order_planner.py @@ -55,7 +55,7 @@ class FakePartner(): @property def date_localization(self): - if not hasattr(self, 'date_localization'): + if not hasattr(self, 'date_localization') and self.date_localization: self.date_localization = 'TODAY!' # The fast way. if ZipcodeSearchEngine and self.zip: diff --git a/stock_landed_costs_average/__init__.py b/stock_landed_costs_average/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/stock_landed_costs_average/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_landed_costs_average/__manifest__.py b/stock_landed_costs_average/__manifest__.py new file mode 100644 index 00000000..1c02704e --- /dev/null +++ b/stock_landed_costs_average/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'Landed Costs Average', + 'summary': 'Use Landed Costs on Average Cost inventory.', + 'version': '11.0.1.0.0', + 'author': "Hibou Corp. ", + 'category': 'Warehouse', + 'license': 'AGPL-3', + 'complexity': 'expert', + 'images': [], + 'website': "https://hibou.io", + 'description': """ +""", + 'depends': [ + 'stock_landed_costs', + ], + 'demo': [], + 'data': [ + ], + 'auto_install': False, + 'installable': True, +} diff --git a/stock_landed_costs_average/models/__init__.py b/stock_landed_costs_average/models/__init__.py new file mode 100644 index 00000000..e9907b68 --- /dev/null +++ b/stock_landed_costs_average/models/__init__.py @@ -0,0 +1 @@ +from . import stock_landed_cost diff --git a/stock_landed_costs_average/models/stock_landed_cost.py b/stock_landed_costs_average/models/stock_landed_cost.py new file mode 100644 index 00000000..19c761f1 --- /dev/null +++ b/stock_landed_costs_average/models/stock_landed_cost.py @@ -0,0 +1,96 @@ +from odoo import api, models, _ +from odoo.exceptions import UserError +import logging + +_logger = logging.getLogger(__name__) + + +class LandedCost(models.Model): + _inherit = 'stock.landed.cost' + + def get_valuation_lines(self): + """ + Override for allowing Average value inventory. + :return: list of new line values + """ + lines = [] + + for move in self.mapped('picking_ids').mapped('move_lines'): + # Only allow for real time valuated products with 'average' or 'fifo' cost + if move.product_id.valuation != 'real_time' or move.product_id.cost_method not in ('fifo', 'average'): + continue + vals = { + 'product_id': move.product_id.id, + 'move_id': move.id, + 'quantity': move.product_qty, + 'former_cost': move.value, + 'weight': move.product_id.weight * move.product_qty, + 'volume': move.product_id.volume * move.product_qty + } + lines.append(vals) + + if not lines and self.mapped('picking_ids'): + raise UserError(_('The selected picking does not contain any move that would be impacted by landed costs. Landed costs are only possible for products configured in real time valuation with real price costing method. Please make sure it is the case, or you selected the correct picking')) + return lines + + @api.multi + def button_validate(self): + """ + Override to directly set new standard_price on product if average costed. + :return: True + """ + if any(cost.state != 'draft' for cost in self): + raise UserError(_('Only draft landed costs can be validated')) + if any(not cost.valuation_adjustment_lines for cost in self): + raise UserError(_('No valuation adjustments lines. You should maybe recompute the landed costs.')) + if not self._check_sum(): + raise UserError(_('Cost and adjustments lines do not match. You should maybe recompute the landed costs.')) + + for cost in self: + move = self.env['account.move'] + move_vals = { + 'journal_id': cost.account_journal_id.id, + 'date': cost.date, + 'ref': cost.name, + 'line_ids': [], + } + for line in cost.valuation_adjustment_lines.filtered(lambda line: line.move_id): + # Prorate the value at what's still in stock + _logger.warn('(line.move_id.remaining_qty / line.move_id.product_qty) * line.additional_landed_cost') + _logger.warn('(%s / %s) * %s' % (line.move_id.remaining_qty, line.move_id.product_qty, line.additional_landed_cost)) + cost_to_add = (line.move_id.remaining_qty / line.move_id.product_qty) * line.additional_landed_cost + _logger.warn('cost_to_add: ' + str(cost_to_add)) + + new_landed_cost_value = line.move_id.landed_cost_value + line.additional_landed_cost + line.move_id.write({ + 'landed_cost_value': new_landed_cost_value, + 'value': line.move_id.value + cost_to_add, + 'remaining_value': line.move_id.remaining_value + cost_to_add, + 'price_unit': (line.move_id.value + new_landed_cost_value) / line.move_id.product_qty, + }) + # `remaining_qty` is negative if the move is out and delivered products that were not + # in stock. + qty_out = 0 + if line.move_id._is_in(): + qty_out = line.move_id.product_qty - line.move_id.remaining_qty + elif line.move_id._is_out(): + qty_out = line.move_id.product_qty + move_vals['line_ids'] += line._create_accounting_entries(move, qty_out) + + # Need to set the standard price directly on the product. + if line.product_id.cost_method == 'average': + # From product.do_change_standard_price + quant_locs = self.env['stock.quant'].sudo().read_group([('product_id', '=', line.product_id.id)], + ['location_id'], ['location_id']) + quant_loc_ids = [loc['location_id'][0] for loc in quant_locs] + locations = self.env['stock.location'].search( + [('usage', '=', 'internal'), ('company_id', '=', self.env.user.company_id.id), + ('id', 'in', quant_loc_ids)]) + qty_available = line.product_id.with_context(location=locations.ids).qty_available + total_cost = (qty_available * line.product_id.standard_price) + cost_to_add + line.product_id.write({'standard_price': total_cost / qty_available}) + + move = move.create(move_vals) + cost.write({'state': 'done', 'account_move_id': move.id}) + move.post() + return True \ No newline at end of file diff --git a/stock_landed_costs_average/tests/__init__.py b/stock_landed_costs_average/tests/__init__.py new file mode 100644 index 00000000..68fef37a --- /dev/null +++ b/stock_landed_costs_average/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_landed_cost diff --git a/stock_landed_costs_average/tests/test_stock_landed_cost.py b/stock_landed_costs_average/tests/test_stock_landed_cost.py new file mode 100644 index 00000000..240a7bf7 --- /dev/null +++ b/stock_landed_costs_average/tests/test_stock_landed_cost.py @@ -0,0 +1,102 @@ +from odoo.addons.stock_landed_costs.tests.test_stock_landed_costs_purchase import TestLandedCosts + + +class TestLandedCostsAverage(TestLandedCosts): + + def setUp(self): + super(TestLandedCostsAverage, self).setUp() + self.product_refrigerator.cost_method = 'average' + self.product_oven.cost_method = 'average' + + def test_00_landed_costs_on_incoming_shipment(self): + original_standard_price = self.product_refrigerator.standard_price + super(TestLandedCostsAverage, self).test_00_landed_costs_on_incoming_shipment() + self.assertTrue(original_standard_price != self.product_refrigerator.standard_price) + + def test_01_landed_costs_simple_average(self): + self.assertEqual(self.product_refrigerator.standard_price, 1.0) + self.assertEqual(self.product_refrigerator.qty_available, 0.0) + picking_in = self.Picking.create({ + 'partner_id': self.supplier_id, + 'picking_type_id': self.picking_type_in_id, + 'location_id': self.supplier_location_id, + 'location_dest_id': self.stock_location_id}) + self.Move.create({ + 'name': self.product_refrigerator.name, + 'product_id': self.product_refrigerator.id, + 'product_uom_qty': 5, + 'product_uom': self.product_refrigerator.uom_id.id, + 'picking_id': picking_in.id, + 'location_id': self.supplier_location_id, + 'location_dest_id': self.stock_location_id}) + picking_in.action_confirm() + res_dict = picking_in.button_validate() + wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')) + wizard.process() + self.assertEqual(self.product_refrigerator.standard_price, 1.0) + self.assertEqual(self.product_refrigerator.qty_available, 5.0) + + stock_landed_cost = self._create_landed_costs({ + 'equal_price_unit': 50, + 'quantity_price_unit': 0, + 'weight_price_unit': 0, + 'volume_price_unit': 0}, picking_in) + stock_landed_cost.compute_landed_cost() + stock_landed_cost.button_validate() + account_entry = self.env['account.move.line'].read_group( + [('move_id', '=', stock_landed_cost.account_move_id.id)], ['debit', 'credit', 'move_id'], ['move_id'])[0] + self.assertEqual(account_entry['debit'], 50.0, 'Wrong Account Entry') + self.assertEqual(self.product_refrigerator.standard_price, 11.0) + + def test_02_landed_costs_average(self): + self.assertEqual(self.product_refrigerator.standard_price, 1.0) + self.assertEqual(self.product_refrigerator.qty_available, 0.0) + picking_in = self.Picking.create({ + 'partner_id': self.supplier_id, + 'picking_type_id': self.picking_type_in_id, + 'location_id': self.supplier_location_id, + 'location_dest_id': self.stock_location_id}) + self.Move.create({ + 'name': self.product_refrigerator.name, + 'product_id': self.product_refrigerator.id, + 'product_uom_qty': 5, + 'product_uom': self.product_refrigerator.uom_id.id, + 'picking_id': picking_in.id, + 'location_id': self.supplier_location_id, + 'location_dest_id': self.stock_location_id}) + picking_in.action_confirm() + res_dict = picking_in.button_validate() + wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')) + wizard.process() + self.assertEqual(self.product_refrigerator.standard_price, 1.0) + self.assertEqual(self.product_refrigerator.qty_available, 5.0) + + picking_out = self.Picking.create({ + 'partner_id': self.customer_id, + 'picking_type_id': self.picking_type_out_id, + 'location_id': self.stock_location_id, + 'location_dest_id': self.customer_location_id}) + self.Move.create({ + 'name': self.product_refrigerator.name, + 'product_id': self.product_refrigerator.id, + 'product_uom_qty': 2, + 'product_uom': self.product_refrigerator.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.stock_location_id, + 'location_dest_id': self.customer_location_id}) + picking_out.action_confirm() + picking_out.action_assign() + res_dict = picking_out.button_validate() + wizard = self.env[(res_dict.get('res_model'))].browse(res_dict.get('res_id')) + wizard.process() + self.assertEqual(self.product_refrigerator.standard_price, 1.0) + self.assertEqual(self.product_refrigerator.qty_available, 3.0) + + stock_landed_cost = self._create_landed_costs({ + 'equal_price_unit': 50, + 'quantity_price_unit': 0, + 'weight_price_unit': 0, + 'volume_price_unit': 0}, picking_in) + stock_landed_cost.compute_landed_cost() + stock_landed_cost.button_validate() + self.assertEqual(self.product_refrigerator.standard_price, 11.0)
Changing Date Planned alone should be done on any existing Pickings or POs.