Decompose get_invoice_line and get_move
to ease the customization of invoice and shipment methods. issue5059 review16751002
This commit is contained in:
1 parent
fa449aafd9
commit
bd04a1964a
1 file changed
+122
-90
+122
-90
@@ -652,17 +652,6 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
'total_amount_cache': purchase.total_amount,
|
||||
})
|
||||
|
||||
def _get_invoice_line_purchase_line(self, invoice_type):
|
||||
'''
|
||||
Return invoice line for each purchase lines
|
||||
'''
|
||||
res = {}
|
||||
for line in self.lines:
|
||||
val = line.get_invoice_line(invoice_type)
|
||||
if val:
|
||||
res[line.id] = val
|
||||
return res
|
||||
|
||||
def _get_invoice_purchase(self, invoice_type):
|
||||
'''
|
||||
Return invoice of type invoice_type
|
||||
@@ -704,7 +693,11 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
self.raise_user_error('missing_account_payable',
|
||||
error_args=(self.party.rec_name,))
|
||||
|
||||
invoice_lines = self._get_invoice_line_purchase_line(invoice_type)
|
||||
invoice_lines = {}
|
||||
for line in self.lines:
|
||||
ilines = line.get_invoice_line(invoice_type)
|
||||
if ilines:
|
||||
invoice_lines[line.id] = ilines
|
||||
if not invoice_lines:
|
||||
return
|
||||
|
||||
@@ -721,14 +714,16 @@ class Purchase(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
'''
|
||||
Create move for each purchase lines
|
||||
'''
|
||||
new_moves = []
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
|
||||
moves = []
|
||||
for line in self.lines:
|
||||
if (line.quantity >= 0) != (move_type == 'in'):
|
||||
continue
|
||||
move = line.create_move()
|
||||
move = line.get_move(move_type)
|
||||
if move:
|
||||
new_moves.append(move)
|
||||
return new_moves
|
||||
moves.append(move)
|
||||
Move.save(moves)
|
||||
return moves
|
||||
|
||||
def _get_return_shipment(self):
|
||||
ShipmentInReturn = Pool().get('stock.shipment.in.return')
|
||||
@@ -1212,51 +1207,15 @@ class PurchaseLine(ModelSQL, ModelView):
|
||||
invoice_line.note = self.note
|
||||
invoice_line.origin = self
|
||||
if self.type != 'line':
|
||||
if (self.purchase.invoice_method == 'order'
|
||||
and not self.invoice_lines
|
||||
and ((all(l.quantity >= 0 for l in self.purchase.lines
|
||||
if l.type == 'line')
|
||||
and invoice_type == 'in_invoice')
|
||||
or (all(l.quantity <= 0 for l in self.purchase.lines
|
||||
if l.type == 'line')
|
||||
and invoice_type == 'in_credit_note'))):
|
||||
if self._get_invoice_not_line(invoice_type):
|
||||
return [invoice_line]
|
||||
else:
|
||||
return []
|
||||
if (invoice_type == 'in_invoice') != (self.quantity >= 0):
|
||||
return []
|
||||
|
||||
stock_moves = []
|
||||
if (self.purchase.invoice_method == 'order'
|
||||
or not self.product
|
||||
or self.product.type == 'service'):
|
||||
quantity = abs(self.quantity)
|
||||
stock_moves = self.moves
|
||||
else:
|
||||
quantity = 0.0
|
||||
for move in self.moves:
|
||||
if move.state == 'done':
|
||||
quantity += Uom.compute_qty(move.uom, move.quantity,
|
||||
self.unit)
|
||||
if move.invoiced_quantity < move.quantity:
|
||||
stock_moves.append(move)
|
||||
invoice_line.stock_moves = stock_moves
|
||||
|
||||
skip_ids = set(l.id for i in self.purchase.invoices_recreated
|
||||
for l in i.lines)
|
||||
for old_invoice_line in self.invoice_lines:
|
||||
if old_invoice_line.type != 'line':
|
||||
continue
|
||||
if old_invoice_line.id not in skip_ids:
|
||||
if old_invoice_line.invoice:
|
||||
old_invoice_type = old_invoice_line.invoice.type
|
||||
else:
|
||||
old_invoice_type = old_invoice_line.invoice_type
|
||||
|
||||
sign = 1 if old_invoice_type == invoice_type else -1
|
||||
|
||||
quantity -= Uom.compute_qty(old_invoice_line.unit,
|
||||
sign * old_invoice_line.quantity, self.unit)
|
||||
quantity = (self._get_invoice_line_quantity(invoice_type)
|
||||
- self._get_invoiced_quantity(invoice_type))
|
||||
|
||||
rounding = self.unit.rounding if self.unit else 0.01
|
||||
invoice_line.quantity = Uom.round(quantity, rounding)
|
||||
@@ -1283,26 +1242,72 @@ class PurchaseLine(ModelSQL, ModelView):
|
||||
if not invoice_line.account:
|
||||
self.raise_user_error('missing_account_expense_property',
|
||||
{'purchase': self.purchase.rec_name})
|
||||
invoice_line.stock_moves = self._get_invoice_line_moves(invoice_type)
|
||||
return [invoice_line]
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return [
|
||||
('/form//field[@name="note"]|/form//field[@name="description"]',
|
||||
'spell', Eval('_parent_purchase', {}).get('party_lang'))]
|
||||
def _get_invoice_not_line(self, invoice_type):
|
||||
'Return if the not line should be invoiced'
|
||||
return (self.purchase.invoice_method == 'order'
|
||||
and not self.invoice_lines
|
||||
and ((all(l.quantity >= 0 for l in self.purchase.lines
|
||||
if l.type == 'line')
|
||||
and invoice_type == 'in_invoice')
|
||||
or (all(l.quantity <= 0 for l in self.purchase.lines
|
||||
if l.type == 'line')
|
||||
and invoice_type == 'in_credit_note')))
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = default.copy()
|
||||
default['moves'] = None
|
||||
default['moves_ignored'] = None
|
||||
default['moves_recreated'] = None
|
||||
default['invoice_lines'] = None
|
||||
return super(PurchaseLine, cls).copy(lines, default=default)
|
||||
def _get_invoice_line_quantity(self, invoice_type):
|
||||
'Return the quantity that should be invoiced'
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
def get_move(self):
|
||||
if (self.purchase.invoice_method == 'order'
|
||||
or not self.product
|
||||
or self.product.type == 'service'):
|
||||
return abs(self.quantity)
|
||||
elif self.purchase.invoice_method == 'shipment':
|
||||
quantity = 0.0
|
||||
for move in self.moves:
|
||||
if move.state == 'done':
|
||||
quantity += Uom.compute_qty(move.uom, move.quantity,
|
||||
self.unit)
|
||||
return quantity
|
||||
|
||||
def _get_invoiced_quantity(self, invoice_type):
|
||||
'Return the quantity already invoiced'
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
quantity = 0
|
||||
skips = {l for i in self.purchase.invoices_recreated for l in i.lines}
|
||||
for invoice_line in self.invoice_lines:
|
||||
if invoice_line.type != 'line':
|
||||
continue
|
||||
if invoice_line not in skips:
|
||||
if invoice_line.invoice:
|
||||
line_type = invoice_line.invoice.type
|
||||
else:
|
||||
line_type = invoice_line.invoice_type
|
||||
|
||||
sign = 1 if line_type == invoice_type else -1
|
||||
|
||||
quantity += Uom.compute_qty(invoice_line.unit,
|
||||
sign * invoice_line.quantity, self.unit)
|
||||
return quantity
|
||||
|
||||
def _get_invoice_line_moves(self, invoice_type):
|
||||
'Return the stock moves that should be invoiced'
|
||||
moves = []
|
||||
if self.purchase.invoice_method == 'order':
|
||||
moves.extend(self.moves)
|
||||
elif self.purchase.invoice_method == 'shipment':
|
||||
for move in self.moves:
|
||||
if move.state == 'done':
|
||||
if move.invoiced_quantity < move.quantity:
|
||||
moves.append(move)
|
||||
return moves
|
||||
|
||||
def get_move(self, move_type):
|
||||
'''
|
||||
Return move values for purchase line
|
||||
'''
|
||||
@@ -1316,12 +1321,12 @@ class PurchaseLine(ModelSQL, ModelView):
|
||||
return
|
||||
if self.product.type == 'service':
|
||||
return
|
||||
skip = set(self.moves_recreated)
|
||||
quantity = abs(self.quantity)
|
||||
for move in self.moves:
|
||||
if move not in skip:
|
||||
quantity -= Uom.compute_qty(move.uom, move.quantity,
|
||||
self.unit)
|
||||
|
||||
if (self.quantity >= 0) != (move_type == 'in'):
|
||||
return
|
||||
|
||||
quantity = (self._get_move_quantity(move_type)
|
||||
- self._get_shipped_quantity(move_type))
|
||||
|
||||
quantity = Uom.round(quantity, self.unit.rounding)
|
||||
if quantity <= 0:
|
||||
@@ -1343,20 +1348,47 @@ class PurchaseLine(ModelSQL, ModelView):
|
||||
move.unit_price = self.unit_price
|
||||
move.currency = self.purchase.currency
|
||||
move.planned_date = self.delivery_date
|
||||
move.invoice_lines = [l for l in self.invoice_lines
|
||||
if not l.stock_moves]
|
||||
move.invoice_lines = self._get_move_invoice_lines(move_type)
|
||||
move.origin = self
|
||||
return move
|
||||
|
||||
def create_move(self):
|
||||
'''
|
||||
Create move line
|
||||
'''
|
||||
move = self.get_move()
|
||||
if not move:
|
||||
return
|
||||
move.save()
|
||||
return move
|
||||
def _get_move_quantity(self, move_type):
|
||||
'Return the quantity that should be shipped'
|
||||
return abs(self.quantity)
|
||||
|
||||
def _get_shipped_quantity(self, move_type):
|
||||
'Return the quantity already shipped'
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
quantity = 0
|
||||
skip = set(self.moves_recreated)
|
||||
for move in self.moves:
|
||||
if move not in skip:
|
||||
quantity += Uom.compute_qty(move.uom, move.quantity,
|
||||
self.unit)
|
||||
return quantity
|
||||
|
||||
def _get_move_invoice_lines(self, move_type):
|
||||
'Return the invoice lines that should be shipped'
|
||||
return [l for l in self.invoice_lines if not l.stock_moves]
|
||||
|
||||
@classmethod
|
||||
def view_attributes(cls):
|
||||
return [
|
||||
('/form//field[@name="note"]|/form//field[@name="description"]',
|
||||
'spell', Eval('_parent_purchase', {}).get('party_lang'))]
|
||||
|
||||
@classmethod
|
||||
def copy(cls, lines, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = default.copy()
|
||||
default['moves'] = None
|
||||
default['moves_ignored'] = None
|
||||
default['moves_recreated'] = None
|
||||
default['invoice_lines'] = None
|
||||
return super(PurchaseLine, cls).copy(lines, default=default)
|
||||
|
||||
|
||||
class PurchaseLineTax(ModelSQL):
|
||||
|
||||
Reference in new issue
Block a user