@@ -1,3 +1,4 @@
|
||||
* Add a button to create invoices manually
|
||||
|
||||
Version 6.8.0 - 2023-05-01
|
||||
--------------------------
|
||||
|
||||
@@ -306,6 +306,12 @@ class Purchase(
|
||||
'tryton-forward', 'tryton-refresh'),
|
||||
'depends': ['state'],
|
||||
},
|
||||
'manual_invoice': {
|
||||
'invisible': (
|
||||
(Eval('invoice_method') != 'manual')
|
||||
| ~Eval('state').in_(['processing', 'done'])),
|
||||
'depends': ['invoice_method', 'state'],
|
||||
},
|
||||
'handle_invoice_exception': {
|
||||
'invisible': ((Eval('invoice_state') != 'exception')
|
||||
| (Eval('state') == 'cancelled')),
|
||||
@@ -763,8 +769,9 @@ class Purchase(
|
||||
|
||||
def create_invoice(self):
|
||||
'Create an invoice for the purchase and return it'
|
||||
|
||||
if self.invoice_method == 'manual':
|
||||
context = Transaction().context
|
||||
if (self.invoice_method == 'manual'
|
||||
and not context.get('_purchase_manual_invoice', False)):
|
||||
return
|
||||
|
||||
invoice_lines = []
|
||||
@@ -998,6 +1005,13 @@ class Purchase(
|
||||
if done:
|
||||
cls.do(done)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
def manual_invoice(cls, purchases):
|
||||
purchases = [p for p in purchases if p.invoice_method == 'manual']
|
||||
with Transaction().set_context(_purchase_manual_invoice=True):
|
||||
cls.process(purchases)
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('purchase.wizard_modify_header')
|
||||
def modify_header(cls, purchases):
|
||||
@@ -1670,7 +1684,8 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
|
||||
def _get_invoice_not_line(self):
|
||||
'Return if the not line should be invoiced'
|
||||
return (self.purchase.invoice_method == 'order'
|
||||
return (
|
||||
self.purchase.invoice_method in {'order', 'manual'}
|
||||
and not self.invoice_lines)
|
||||
|
||||
def _get_invoice_line_quantity(self):
|
||||
@@ -1678,7 +1693,7 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
if (self.purchase.invoice_method == 'order'
|
||||
if (self.purchase.invoice_method in {'order', 'manual'}
|
||||
or not self.product
|
||||
or self.product.type == 'service'):
|
||||
return self.quantity
|
||||
@@ -1717,7 +1732,7 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
def _get_invoice_line_moves(self):
|
||||
'Return the stock moves that should be invoiced'
|
||||
moves = []
|
||||
if self.purchase.invoice_method == 'order':
|
||||
if self.purchase.invoice_method in {'order', 'manual'}:
|
||||
moves.extend(self.moves)
|
||||
elif self.purchase.invoice_method == 'shipment':
|
||||
for move in self.moves:
|
||||
@@ -1790,7 +1805,7 @@ class Line(sequence_ordered(), ModelSQL, ModelView):
|
||||
|
||||
def _get_move_invoice_lines(self, move_type):
|
||||
'Return the invoice lines that should be shipped'
|
||||
if self.purchase.invoice_method == 'order':
|
||||
if self.purchase.invoice_method in {'order', 'manual'}:
|
||||
return [l for l in self.invoice_lines]
|
||||
else:
|
||||
return [l for l in self.invoice_lines if not l.stock_moves]
|
||||
|
||||
@@ -344,6 +344,20 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="group" ref="group_purchase_admin"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_manual_invoice_button">
|
||||
<field name="name">manual_invoice</field>
|
||||
<field name="string">Create Invoice</field>
|
||||
<field name="model" search="[('model', '=', 'purchase.purchase')]"/>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="purchase_manual_button_group_purchase">
|
||||
<field name="button" ref="purchase_manual_invoice_button"/>
|
||||
<field name="group" ref="group_purchase"/>
|
||||
</record>
|
||||
<record model="ir.model.button-res.group" id="purchase_manual_button_group_account">
|
||||
<field name="button" ref="purchase_manual_invoice_button"/>
|
||||
<field name="group" ref="account.group_account"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.model.button" id="purchase_modify_header_button">
|
||||
<field name="name">modify_header</field>
|
||||
<field name="string">Modify Header</field>
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
================================
|
||||
Purchase Manual Invoice Scenario
|
||||
================================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, get_accounts)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('purchase')
|
||||
|
||||
>>> Party = Model.get('party.party')
|
||||
>>> ProductCategory = Model.get('product.category')
|
||||
>>> ProductTemplate = Model.get('product.template')
|
||||
>>> ProductUom = Model.get('product.uom')
|
||||
>>> Purchase = Model.get('purchase.purchase')
|
||||
|
||||
Create company::
|
||||
|
||||
>>> _ = create_company()
|
||||
|
||||
Create chart of accounts::
|
||||
|
||||
>>> _ = create_chart()
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> supplier = Party(name='Supplier')
|
||||
>>> supplier.save()
|
||||
|
||||
Create account category::
|
||||
|
||||
>>> account_category = ProductCategory(name="Account Category")
|
||||
>>> account_category.accounting = True
|
||||
>>> account_category.account_expense = accounts['expense']
|
||||
>>> account_category.save()
|
||||
|
||||
Create product::
|
||||
|
||||
>>> unit, = ProductUom.find([('name', '=', 'Unit')])
|
||||
|
||||
>>> template = ProductTemplate()
|
||||
>>> template.name = 'product'
|
||||
>>> template.default_uom = unit
|
||||
>>> template.type = 'service'
|
||||
>>> template.purchasable = True
|
||||
>>> template.account_category = account_category
|
||||
>>> product, = template.products
|
||||
>>> product.cost_price = Decimal('5')
|
||||
>>> template.save()
|
||||
>>> product, = template.products
|
||||
|
||||
Purchase with manual invoice method::
|
||||
|
||||
>>> purchase = Purchase()
|
||||
>>> purchase.party = supplier
|
||||
>>> purchase.invoice_method = 'manual'
|
||||
>>> line = purchase.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 10
|
||||
>>> purchase.click('quote')
|
||||
>>> purchase.click('confirm')
|
||||
>>> purchase.state
|
||||
'done'
|
||||
>>> purchase.invoice_state
|
||||
'none'
|
||||
>>> len(purchase.invoices)
|
||||
0
|
||||
|
||||
Manually create an invoice::
|
||||
|
||||
>>> purchase.click('manual_invoice')
|
||||
>>> purchase.state
|
||||
'processing'
|
||||
>>> purchase.invoice_state
|
||||
'pending'
|
||||
|
||||
Change quantity on invoice and create a new invoice::
|
||||
|
||||
>>> invoice, = purchase.invoices
|
||||
>>> line, = invoice.lines
|
||||
>>> line.quantity = 5
|
||||
>>> invoice.save()
|
||||
|
||||
>>> len(purchase.invoices)
|
||||
1
|
||||
>>> purchase.click('manual_invoice')
|
||||
>>> len(purchase.invoices)
|
||||
2
|
||||
@@ -77,6 +77,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
||||
<button name="confirm" icon="tryton-ok"/>
|
||||
<button name="process"/>
|
||||
<button name="manual_invoice" icon="tryton-forward"/>
|
||||
</group>
|
||||
<field name="party_lang" invisible="1" colspan="6"/>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user