summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Martinelli <nim@odoo.com>2016-03-07 11:58:30 +0100
committerNicolas Martinelli <nim@odoo.com>2016-03-07 14:02:44 +0100
commitb30afcbdb98fa6b444035a3bb43707f734bd07b5 (patch)
tree3231e09d59fe649b599a307db2d0c40a2689d74f
parentfcc733241cd115f362a65afc759195e80c784978 (diff)
[IMP] sale_stock: set partially delivered products to fully invoiced
-rw-r--r--addons/sale_stock/sale_stock.py16
-rw-r--r--addons/sale_stock/tests/test_sale_stock.py52
2 files changed, 68 insertions, 0 deletions
diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py
index 0387dbd6783..9c7fe4bea02 100644
--- a/addons/sale_stock/sale_stock.py
+++ b/addons/sale_stock/sale_stock.py
@@ -95,6 +95,22 @@ class SaleOrderLine(models.Model):
route_id = fields.Many2one('stock.location.route', string='Route', domain=[('sale_selectable', '=', True)])
product_tmpl_id = fields.Many2one('product.template', related='product_id.product_tmpl_id', string='Product Template', readonly=True)
+ @api.depends('order_id.state')
+ def _compute_invoice_status(self):
+ super(SaleOrderLine, self)._compute_invoice_status()
+ for line in self:
+ # We handle the following specific situation: a physical product is partially delivered,
+ # but we would like to set its invoice status to 'Fully Invoiced'. The use case is for
+ # products sold by weight, where the delivered quantity rarely matches exactly the
+ # quantity ordered.
+ if line.order_id.state == 'done'\
+ and line.invoice_status == 'no'\
+ and line.product_id.type in ['consu', 'product']\
+ and line.product_id.invoice_policy == 'delivery'\
+ and line.procurement_ids.mapped('move_ids')\
+ and all(move.state in ['done', 'cancel'] for move in line.procurement_ids.mapped('move_ids')):
+ line.invoice_status = 'invoiced'
+
@api.multi
@api.depends('product_id')
def _compute_qty_delivered_updateable(self):
diff --git a/addons/sale_stock/tests/test_sale_stock.py b/addons/sale_stock/tests/test_sale_stock.py
index 95874b67d7b..368e552096d 100644
--- a/addons/sale_stock/tests/test_sale_stock.py
+++ b/addons/sale_stock/tests/test_sale_stock.py
@@ -165,3 +165,55 @@ class TestSaleStock(TestSale):
# Check invoice
self.assertEqual(self.so.invoice_status, 'invoiced', 'Sale Stock: so invoice_status should be "invoiced" after picking return')
+
+ def test_03_sale_stock_delivery_partial(self):
+ """
+ Test a SO with a product invoiced on delivery. Deliver partially and invoice the SO, when
+ the SO is set on 'done', the SO should be fully invoiced.
+ """
+ # intial so
+ self.partner = self.env.ref('base.res_partner_1')
+ self.product = self.env.ref('product.product_product_47')
+ so_vals = {
+ 'partner_id': self.partner.id,
+ 'partner_invoice_id': self.partner.id,
+ 'partner_shipping_id': self.partner.id,
+ 'order_line': [(0, 0, {
+ 'name': self.product.name,
+ 'product_id': self.product.id,
+ 'product_uom_qty': 5.0,
+ 'product_uom': self.product.uom_id.id,
+ 'price_unit': self.product.list_price})],
+ 'pricelist_id': self.env.ref('product.list0').id,
+ }
+ self.so = self.env['sale.order'].create(so_vals)
+
+ # confirm our standard so, check the picking
+ self.so.action_confirm()
+ self.assertTrue(self.so.picking_ids, 'Sale Stock: no picking created for "invoice on delivery" stockable products')
+
+ # invoice in on delivery, nothing should be invoiced
+ self.assertEqual(self.so.invoice_status, 'no', 'Sale Stock: so invoice_status should be "nothing to invoice"')
+
+ # deliver partially
+ pick = self.so.picking_ids
+ pick.force_assign()
+ pick.pack_operation_product_ids.write({'qty_done': 4})
+ backorder_wiz_id = pick.do_new_transfer()['res_id']
+ backorder_wiz = self.env['stock.backorder.confirmation'].browse([backorder_wiz_id])
+ backorder_wiz.process_cancel_backorder()
+
+ # Check quantity delivered
+ del_qty = sum(sol.qty_delivered for sol in self.so.order_line)
+ self.assertEqual(del_qty, 4.0, 'Sale Stock: delivered quantity should be 4.0 after partial delivery')
+
+ # Check invoice
+ self.assertEqual(self.so.invoice_status, 'to invoice', 'Sale Stock: so invoice_status should be "to invoice" before invoicing')
+ inv_1_id = self.so.action_invoice_create()
+ self.assertEqual(self.so.invoice_status, 'no', 'Sale Stock: so invoice_status should be "no" after invoicing')
+ self.assertEqual(len(inv_1_id), 1, 'Sale Stock: only one invoice should be created')
+ self.inv_1 = self.env['account.invoice'].browse(inv_1_id)
+ self.assertEqual(self.inv_1.amount_untaxed, self.inv_1.amount_untaxed, 'Sale Stock: amount in SO and invoice should be the same')
+
+ self.so.action_done()
+ self.assertEqual(self.so.invoice_status, 'invoiced', 'Sale Stock: so invoice_status should be "invoiced" when set to done')