diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 1e2010aff3f..c3001c510dd 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -70,6 +70,13 @@ class AccountMove(models.Model): if self.env.context.get('default_journal_type'): return self.env['account.journal'].search([('type', '=', self.env.context['default_journal_type'])], limit=1).id + @api.multi + @api.depends('line_ids.partner_id') + def _compute_partner_id(self): + for move in self: + partner = move.line_ids.mapped('partner_id') + move.partner_id = partner.id if len(partner) == 1 else False + name = fields.Char(string='Number', required=True, copy=False, default='/') ref = fields.Char(string='Reference', copy=False) date = fields.Date(required=True, states={'posted': [('readonly', True)]}, index=True, default=fields.Date.context_today) @@ -84,7 +91,7 @@ class AccountMove(models.Model): 'in \'Posted\' status.') line_ids = fields.One2many('account.move.line', 'move_id', string='Journal Items', states={'posted': [('readonly', True)]}, copy=True) - partner_id = fields.Many2one('res.partner', related='line_ids.partner_id', string="Partner", store=True, readonly=True) + partner_id = fields.Many2one('res.partner', compute='_compute_partner_id', string="Partner", store=True, readonly=True) amount = fields.Monetary(compute='_amount_compute', store=True) narration = fields.Text(string='Internal Note') company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', store=True, readonly=True, diff --git a/addons/account_asset/account_asset_invoice.py b/addons/account_asset/account_asset_invoice.py index bb4e2f90d92..078978f591d 100644 --- a/addons/account_asset/account_asset_invoice.py +++ b/addons/account_asset/account_asset_invoice.py @@ -60,7 +60,7 @@ class AccountInvoiceLine(models.Model): 'partner_id': self.invoice_id.partner_id.id, 'company_id': self.invoice_id.company_id.id, 'currency_id': self.invoice_id.company_currency_id.id, - 'date': self.asset_start_date or self.invoice_id.date_invoice, + 'date': self.invoice_id.date_invoice, 'invoice_id': self.invoice_id.id, } changed_vals = self.env['account.asset.asset'].onchange_category_id_values(vals['category_id']) diff --git a/addons/account_asset/views/account_asset_view.xml b/addons/account_asset/views/account_asset_view.xml index 76921306a90..661c0c137bf 100644 --- a/addons/account_asset/views/account_asset_view.xml +++ b/addons/account_asset/views/account_asset_view.xml @@ -24,18 +24,18 @@
-
-
-
diff --git a/addons/account_bank_statement_import/account_bank_statement_import.py b/addons/account_bank_statement_import/account_bank_statement_import.py index 0d512a119c9..75c3a11a6b1 100644 --- a/addons/account_bank_statement_import/account_bank_statement_import.py +++ b/addons/account_bank_statement_import/account_bank_statement_import.py @@ -213,7 +213,11 @@ class AccountBankStatementImport(models.TransientModel): if 'unique_import_id' not in line_vals \ or not line_vals['unique_import_id'] \ or not bool(BankStatementLine.sudo().search([('unique_import_id', '=', line_vals['unique_import_id'])], limit=1)): - filtered_st_lines.append(line_vals) + if line_vals['amount'] != 0: + # Some banks, like ING, create a line for free charges. + # We just skip those lines as there's a 'non-zero' constraint + # on the amount of account.bank.statement.line + filtered_st_lines.append(line_vals) else: ignored_statement_lines_import_ids.append(line_vals['unique_import_id']) if len(filtered_st_lines) > 0: diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index 2d2c9aa2f70..6da3849839f 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -94,7 +94,11 @@ class StockPicking(models.Model): @api.onchange('carrier_id') def onchange_carrier(self): if self.carrier_id.delivery_type in ['fixed', 'base_on_rule']: - self.carrier_price = self.carrier_id.price + order = self.sale_id + if order: + self.carrier_price = self.carrier_id.get_price_available(order) + else: + self.carrier_price = self.carrier_id.price @api.depends('product_id', 'move_lines') def _cal_weight(self): diff --git a/addons/hr_timesheet/hr_timesheet.py b/addons/hr_timesheet/hr_timesheet.py index 14e885a40a3..2faaf91cfd6 100644 --- a/addons/hr_timesheet/hr_timesheet.py +++ b/addons/hr_timesheet/hr_timesheet.py @@ -9,7 +9,7 @@ class AccountAnalyticLine(models.Model): task_id = fields.Many2one('project.task', 'Task') project_id = fields.Many2one('project.project', 'Project') - department_id = fields.Many2one('hr.department', "Department", related='user_id.employee_ids.department_id', store=True) + department_id = fields.Many2one('hr.department', "Department", related='user_id.employee_ids.department_id', store=True, readonly=True) @api.onchange('project_id') def onchange_project_id(self): diff --git a/addons/point_of_sale/security/ir.model.access.csv b/addons/point_of_sale/security/ir.model.access.csv index 17b4f584f45..61b1eebb518 100644 --- a/addons/point_of_sale/security/ir.model.access.csv +++ b/addons/point_of_sale/security/ir.model.access.csv @@ -32,3 +32,5 @@ access_barcode_nomenclature_pos_user,barcode.nomenclature.pos.user,barcodes.mode access_barcode_nomenclature_pos_manager,barcode.nomenclature.pos.manager,barcodes.model_barcode_nomenclature,group_pos_manager,1,1,1,1 access_barcode_rule_pos_user,barcode.rule.pos.user,barcodes.model_barcode_rule,group_pos_user,1,0,0,0 access_barcode_rule_pos_manager,barcode.rule.pos.manager,barcodes.model_barcode_rule,group_pos_manager,1,1,1,1 +access_bank_statement_cashbox_user,account.bank.statement.cashbox user,account.model_account_bank_statement_cashbox,group_pos_user,1,1,1,0 +access_account_cashbox_line_user,account.cashbox.line user,account.model_account_cashbox_line,group_pos_user,1,1,1,0 diff --git a/addons/product_visible_discount/models/sale_order.py b/addons/product_visible_discount/models/sale_order.py index 3c5e95b0e0d..0035a64419f 100644 --- a/addons/product_visible_discount/models/sale_order.py +++ b/addons/product_visible_discount/models/sale_order.py @@ -72,5 +72,6 @@ class SaleOrderLine(models.Model): @api.multi def _get_display_price(self, product): if self.order_id.pricelist_id.discount_policy == 'without_discount': - return product.lst_price + return self.order_id.company_id.currency_id.compute( + product.lst_price, self.order_id.pricelist_id.currency_id) return super(SaleOrderLine, self)._get_display_price(product) diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index eadff5b6346..741f6e7cb6b 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -750,8 +750,8 @@ class StockMove(models.Model): owner_id=pack_operation.owner_id.id, src_package_id=pack_operation.package_id.id, dest_package_id=dest_package_id) if redo_false_quants: - false_quants_move = [x for x in move_rec_updateme.reserved_quant_ids if (not x.lot_id) and (x.owner_id.id == pack_operation.owner_id.id) \ - and (x.location_id.id == pack_operation.location_id.id) and (x.package_id.id != pack_operation.package_id.id)] + false_quants_move = [x for x in move_rec_updateme.reserved_quant_ids if (not x.lot_id) and (x.owner_id.id == pack_operation.owner_id.id) and + (x.location_id.id == pack_operation.location_id.id) and (x.package_id.id == pack_operation.package_id.id)] return True @api.multi diff --git a/addons/stock/tests/common.py b/addons/stock/tests/common.py index 767470368f0..e836c93a0d6 100644 --- a/addons/stock/tests/common.py +++ b/addons/stock/tests/common.py @@ -27,15 +27,16 @@ class TestStockCommon(common.TransactionCase): self.picking_type_out = self.ModelDataObj.xmlid_to_res_id('stock.picking_type_out') self.supplier_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_suppliers') self.stock_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_stock') + self.pack_location = self.ModelDataObj.xmlid_to_res_id('stock.location_pack_zone') self.customer_location = self.ModelDataObj.xmlid_to_res_id('stock.stock_location_customers') self.categ_unit = self.ModelDataObj.xmlid_to_res_id('product.product_uom_categ_unit') self.categ_kgm = self.ModelDataObj.xmlid_to_res_id('product.product_uom_categ_kgm') # Product Created A, B, C, D - self.productA = self.ProductObj.create({'name': 'Product A'}) - self.productB = self.ProductObj.create({'name': 'Product B'}) - self.productC = self.ProductObj.create({'name': 'Product C'}) - self.productD = self.ProductObj.create({'name': 'Product D'}) + self.productA = self.ProductObj.create({'name': 'Product A', 'type': 'product'}) + self.productB = self.ProductObj.create({'name': 'Product B', 'type': 'product'}) + self.productC = self.ProductObj.create({'name': 'Product C', 'type': 'product'}) + self.productD = self.ProductObj.create({'name': 'Product D', 'type': 'product'}) # Configure unit of measure. self.uom_kg = self.UomObj.create({ @@ -90,9 +91,9 @@ class TestStockCommon(common.TransactionCase): 'rounding': 1.0}) # Product for different unit of measure. - self.DozA = self.ProductObj.create({'name': 'Dozon-A', 'uom_id': self.uom_dozen.id, 'uom_po_id': self.uom_dozen.id}) - self.SDozA = self.ProductObj.create({'name': 'SuperDozon-A', 'uom_id': self.uom_sdozen.id, 'uom_po_id': self.uom_sdozen.id}) - self.SDozARound = self.ProductObj.create({'name': 'SuperDozenRound-A', 'uom_id': self.uom_sdozen_round.id, 'uom_po_id': self.uom_sdozen_round.id}) - self.UnitA = self.ProductObj.create({'name': 'Unit-A'}) - self.kgB = self.ProductObj.create({'name': 'kg-B', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id}) - self.gB = self.ProductObj.create({'name': 'g-B', 'uom_id': self.uom_gm.id, 'uom_po_id': self.uom_gm.id}) + self.DozA = self.ProductObj.create({'name': 'Dozon-A', 'type': 'product', 'uom_id': self.uom_dozen.id, 'uom_po_id': self.uom_dozen.id}) + self.SDozA = self.ProductObj.create({'name': 'SuperDozon-A', 'type': 'product', 'uom_id': self.uom_sdozen.id, 'uom_po_id': self.uom_sdozen.id}) + self.SDozARound = self.ProductObj.create({'name': 'SuperDozenRound-A', 'type': 'product', 'uom_id': self.uom_sdozen_round.id, 'uom_po_id': self.uom_sdozen_round.id}) + self.UnitA = self.ProductObj.create({'name': 'Unit-A', 'type': 'product'}) + self.kgB = self.ProductObj.create({'name': 'kg-B', 'type': 'product', 'uom_id': self.uom_kg.id, 'uom_po_id': self.uom_kg.id}) + self.gB = self.ProductObj.create({'name': 'g-B', 'type': 'product', 'uom_id': self.uom_gm.id, 'uom_po_id': self.uom_gm.id}) diff --git a/addons/stock/tests/test_stock_flow.py b/addons/stock/tests/test_stock_flow.py index 06599ddfd11..b7bcad76539 100644 --- a/addons/stock/tests/test_stock_flow.py +++ b/addons/stock/tests/test_stock_flow.py @@ -205,19 +205,21 @@ class TestStockFlow(TestStockCommon): self.assertEqual(move.state, 'confirmed', 'Wrong state of move line.') # Product assign to outgoing shipments picking_out.action_assign() - for move in picking_out.move_lines: - self.assertEqual(move.state, 'assigned', 'Wrong state of move line.') + self.assertEqual(picking_out.move_lines[0].state, 'confirmed', 'Wrong state of move line.') + self.assertEqual(picking_out.move_lines[1].state, 'assigned', 'Wrong state of move line.') + self.assertEqual(picking_out.move_lines[2].state, 'assigned', 'Wrong state of move line.') + self.assertEqual(picking_out.move_lines[3].state, 'confirmed', 'Wrong state of move line.') # Check availability for product A - aval_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)], limit=1).availability + aval_a_qty = self.MoveObj.search([('product_id', '=', self.productA.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(aval_a_qty, 4.0, 'Wrong move quantity availability of product A (%s found instead of 4)' % (aval_a_qty)) # Check availability for product B - aval_b_qty = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)], limit=1).availability + aval_b_qty = self.MoveObj.search([('product_id', '=', self.productB.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(aval_b_qty, 5.0, 'Wrong move quantity availability of product B (%s found instead of 5)' % (aval_b_qty)) # Check availability for product C - aval_c_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)], limit=1).availability + aval_c_qty = self.MoveObj.search([('product_id', '=', self.productC.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(aval_c_qty, 3.0, 'Wrong move quantity availability of product C (%s found instead of 3)' % (aval_c_qty)) # Check availability for product D - aval_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)], limit=1).availability + aval_d_qty = self.MoveObj.search([('product_id', '=', self.productD.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(aval_d_qty, 7.0, 'Wrong move quantity availability of product D (%s found instead of 7)' % (aval_d_qty)) # ---------------------------------------------------------------------- @@ -815,23 +817,21 @@ class TestStockFlow(TestStockCommon): for move in picking_out.move_lines: self.assertEqual(move.state, 'assigned', 'Wrong state of move line.') # Check product A available quantity - DozA_qty = self.MoveObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_out.id)], limit=1).availability + DozA_qty = self.MoveObj.search([('product_id', '=', self.DozA.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(DozA_qty, 4.5, 'Wrong move quantity availability (%s found instead of 4.5)' % (DozA_qty)) # Check product B available quantity - SDozA_qty = self.MoveObj.search([('product_id', '=', self.SDozA.id), ('picking_id', '=', picking_out.id)], limit=1).availability + SDozA_qty = self.MoveObj.search([('product_id', '=', self.SDozA.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(SDozA_qty, 2, 'Wrong move quantity availability (%s found instead of 2)' % (SDozA_qty)) # Check product C available quantity - SDozARound_qty = self.MoveObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_out.id)], limit=1).availability + SDozARound_qty = self.MoveObj.search([('product_id', '=', self.SDozARound.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(SDozARound_qty, 3, 'Wrong move quantity availability (%s found instead of 3)' % (SDozARound_qty)) # Check product D available quantity - gB_qty = self.MoveObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_out.id)], limit=1).availability + gB_qty = self.MoveObj.search([('product_id', '=', self.gB.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(gB_qty, 503, 'Wrong move quantity availability (%s found instead of 503)' % (gB_qty)) # Check product D available quantity - kgB_qty = self.MoveObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_out.id)], limit=1).availability + kgB_qty = self.MoveObj.search([('product_id', '=', self.kgB.id), ('picking_id', '=', picking_out.id)], limit=1).reserved_availability self.assertEqual(kgB_qty, 0.020, 'Wrong move quantity availability (%s found instead of 0.020)' % (kgB_qty)) - picking_out.action_confirm() - picking_out.action_assign() picking_out.do_prepare_partial() picking_out.do_transfer() @@ -1276,4 +1276,137 @@ class TestStockFlow(TestStockCommon): self.assertEqual(sum([x.qty for x in quants if not x.lot_id]), 0.0, 'Wrong sum of quants with no lot') self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot1.id]), 1.0, 'Wrong sum of quants with lot 1') self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot2.id]), 1.0, 'Wrong sum of quants with lot 2') - self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot3.id]), 2.0, 'Wrong sum of quants with lot 3') \ No newline at end of file + self.assertEqual(sum([x.qty for x in quants if x.lot_id.id == lot3.id]), 2.0, 'Wrong sum of quants with lot 3') + + + def test_40_pack_in_pack(self): + """ Put a pack in pack""" + picking_out = self.PickingObj.create({ + 'partner_id': self.partner_agrolite_id, + 'picking_type_id': self.picking_type_out, + 'location_id': self.pack_location, + 'location_dest_id': self.customer_location}) + move_out = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 3, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_out.id, + 'location_id': self.pack_location, + 'location_dest_id': self.customer_location}) + picking_pack = self.PickingObj.create({ + 'partner_id': self.partner_agrolite_id, + 'picking_type_id': self.picking_type_out, + 'location_id': self.stock_location , + 'location_dest_id': self.pack_location}) + move_pack = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 3, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_pack.id, + 'location_id': self.stock_location, + 'location_dest_id': self.pack_location, + 'move_dest_id': move_out.id}) + picking_in = self.PickingObj.create({ + 'partner_id': self.partner_delta_id, + 'picking_type_id': self.picking_type_in, + 'location_id': self.supplier_location, + 'location_dest_id': self.stock_location}) + move_in = self.MoveObj.create({ + 'name': self.productA.name, + 'product_id': self.productA.id, + 'product_uom_qty': 3, + 'product_uom': self.productA.uom_id.id, + 'picking_id': picking_in.id, + 'location_id': self.supplier_location, + 'location_dest_id': self.stock_location, + 'move_dest_id': move_pack.id}) + + # Check incoming shipment move lines state. + for move in picking_in.move_lines: + self.assertEqual(move.state, 'draft', 'Wrong state of move line.') + # Confirm incoming shipment. + picking_in.action_confirm() + # Check incoming shipment move lines state. + for move in picking_in.move_lines: + self.assertEqual(move.state, 'assigned', 'Wrong state of move line.') + + # Check incoming shipment move lines state. + for move in picking_pack.move_lines: + self.assertEqual(move.state, 'draft', 'Wrong state of move line.') + # Confirm incoming shipment. + picking_pack.action_confirm() + # Check incoming shipment move lines state. + for move in picking_pack.move_lines: + self.assertEqual(move.state, 'waiting', 'Wrong state of move line.') + + # Check incoming shipment move lines state. + for move in picking_out.move_lines: + self.assertEqual(move.state, 'draft', 'Wrong state of move line.') + # Confirm incoming shipment. + picking_out.action_confirm() + # Check incoming shipment move lines state. + for move in picking_out.move_lines: + self.assertEqual(move.state, 'waiting', 'Wrong state of move line.') + + # Set the quantity done on the pack operation + picking_in.pack_operation_product_ids.qty_done = 3.0 + # Put in a pack + picking_in.put_in_pack() + # Get the new package + picking_in_package = picking_in.pack_operation_ids.result_package_id + # Validate picking + picking_in.do_new_transfer() + + # Check first picking state changed to done + for move in picking_in.move_lines: + self.assertEqual(move.state, 'done', 'Wrong state of move line.') + # Check next picking state changed to 'assigned' + for move in picking_pack.move_lines: + self.assertEqual(move.state, 'assigned', 'Wrong state of move line.') + + # set the pack in pack operation to 'done' + for pack in picking_pack.pack_operation_pack_ids: + pack.qty_done = 1.0 + + # Put in a pack + picking_pack.put_in_pack() + # Get the new package + picking_pack_package = picking_pack.pack_operation_ids.result_package_id + # Validate picking + picking_pack.do_new_transfer() + + # Check second picking state changed to done + for move in picking_pack.move_lines: + self.assertEqual(move.state, 'done', 'Wrong state of move line.') + # Check next picking state changed to 'assigned' + for move in picking_out.move_lines: + self.assertEqual(move.state, 'assigned', 'Wrong state of move line.') + + # set the pack in pack operation to 'done' + for pack in picking_out.pack_operation_pack_ids: + pack.qty_done = 1.0 + + # Validate picking + picking_out.do_new_transfer() + + # check all pickings are done + for move in picking_in.move_lines: + self.assertEqual(move.state, 'done', 'Wrong state of move line.') + for move in picking_pack.move_lines: + self.assertEqual(move.state, 'done', 'Wrong state of move line.') + for move in picking_out.move_lines: + self.assertEqual(move.state, 'done', 'Wrong state of move line.') + + # Check picking_in_package is in picking_pack_package + self.assertEqual(picking_in_package.parent_id.id, picking_pack_package.id, 'The package created in the picking in is not in the one created in picking pack') + # Check that both packages are in the customer location + self.assertEqual(picking_pack_package.location_id.id, self.customer_location, 'The package created in picking pack is not in the customer location') + self.assertEqual(picking_in_package.location_id.id, self.customer_location, 'The package created in picking in is not in the customer location') + # Check that we have a quant in customer location, for the productA with qty 3 + quant = self.StockQuantObj.search([('location_id', '=', self.customer_location), ('product_id', '=', self.productA.id)]) + self.assertTrue(quant.id, 'There is no quant in customer location for productA') + self.assertEqual(quant.qty, 3.0, 'The quant in customer location for productA has not a quantity of 3.0') + # Check that the parent package of the quant is the picking_in_package + self.assertEqual(quant.package_id.id, picking_in_package.id, 'The quant in customer location is not in its package created in picking in') diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml index 8641f9e0909..166444b3032 100644 --- a/addons/stock/views/product_views.xml +++ b/addons/stock/views/product_views.xml @@ -262,7 +262,7 @@