Welcome | Get started | Dive | Contribute | Topics | Reference | Changes | More
trading
: Product invoices¶
See also The trading plugin.
Snippets in this document are tested on the
lino_book.projects.cosi2
demo project.
>>> from lino import startup
>>> startup('lino_book.projects.cosi2.settings.doctests')
>>> from lino.api.doctest import *
>>> ses = rt.login('robin')
The plugin¶
Lino implements product invoices in the lino_xl.lib.trading
plugin. The internal codename was “sales” until 20240325, we renamed it because you
might generate product invoices for other trade types as well.
The plugin needs and automatically installs the
lino_xl.lib.products
plugin.
It also needs and installs lino_xl.lib.vat
(and not
lino_xl.lib.vatless
). Which means that if you want product invoices, you
cannot not also install the VAT framework. If the site operator is
not subject to VAT, you might add lino_xl.lib.bevats
which hides most of
the VAT functionality.
>>> dd.plugins.trading.needs_plugins
['lino.modlib.memo', 'lino_xl.lib.products', 'lino_xl.lib.vat']
This plugin may be combined with the lino_xl.lib.invoicing
plugin which
adds automatic generation of such product invoices.
Configuration settings¶
- trading.print_items_table¶
The name of a data table to use when printing invoices using appypod print method.
This is a plugin setting.
Product invoices¶
- class lino_xl.lib.trading.VatProductInvoice¶
The Django model representing a product invoice.
Inherits from
lino_xl.lib.accounting.Voucher
,SalesDocument
,Matching
,lino_xl.lib.invoicing.InvoicingTargetVoucher
andStorageTransferer
.Virtual fields:
- balance_before¶
The balance of payments or debts that existed already before this voucher.
On a printed invoice, this amount should be mentioned and added to the invoice’s amount in order to get the total amount to pay.
- balance_to_pay¶
The balance of all movements matching this invoice.
Methods:
- get_print_items(self, ar):
For usage in an appy template:
do text from table(obj.get_print_items(ar))
- class lino_xl.lib.trading.InvoiceItem¶
The Django model representing an item of a product invoice.
- class lino_xl.lib.trading.InvoiceDetail¶
The Lino layout representing the detail view of a product invoice.
- class lino_xl.lib.trading.Invoices¶
- class lino_xl.lib.trading.InvoicesByJournal¶
Shows all invoices of a given journal.
The master instance must be a journal having
VatProductInvoice
aslino_xl.lib.accounting.Journal.voucher_type
.
- class lino_xl.lib.trading.DueInvoices¶
Shows all due product invoices.
- class lino_xl.lib.trading.ProductDocItem¶
Model mixin for voucher items that potentially refer to a product.
- product¶
The product that is being sold or purchased.
- description¶
A multi-line rich text to be printed in the resulting printable document.
- discount¶
The percentage to subtract from the price of this item.
- class lino_xl.lib.trading.ItemsByInvoicePrint¶
The table used to render items in a printable document.
- description_print¶
TODO: write more about it.
- class lino_xl.lib.trading.ItemsByInvoicePrintNoQtyColumn¶
Alternative column layout to be used when printing an invoice.
- class lino_xl.lib.trading.SalesPrintable¶
Inherits from
PartnerPrintable
andCertifiable
.- subject¶
A single-line text that describes this voucher.
- paper_type¶
The type of paper to use when printing this voucher.
- class lino_xl.lib.trading.SalesDocument¶
Common base class for
lino_xl.lib.orders.Order
andVatProductInvoice
.Inherits from
SalesPrintable
andlino_xl.lib.vat.VatVoucher
Subclasses must either add themselves a
date
field (as doesOrder
) or inherit it from Voucher (as doesVatProductInvoice
).This model mixin sets
edit_totals
to False.- intro¶
An optional introduction text to be printed on the document.
- print_items_table¶
The table (column layout) to use in the printed document.
Paper types¶
- class lino_xl.lib.trading.PaperType¶
Describes a paper type (document template) to be used when printing an invoice.
A sample use case is to differentiate between invoices to get printed either on a company letterpaper for expedition via paper mail or into an email-friendly pdf file.
Inherits from
lino.utils.mldbc.mixins.BabelNamed
.- templates_group = 'trading/VatProductInvoice'
A class attribute.
- template¶
Trade types¶
The plugin updates your lino_xl.lib.accounting.TradeTypes.sales
,
causing two additional database fields to be injected to
lino_xl.lib.products.Product
.
The first injected field is the sales price of a product:
>>> translation.activate('en')
>>> print(accounting.TradeTypes.sales.price_field_name)
sales_price
>>> print(accounting.TradeTypes.sales.price_field_label)
Sales price
>>> products.Product._meta.get_field('sales_price')
<lino.core.fields.PriceField: sales_price>
The other injected field is the sales base account of a product:
>>> print(accounting.TradeTypes.sales.base_account_field_name)
sales_account
>>> print(accounting.TradeTypes.sales.base_account_field_label)
Sales account
>>> products.Product._meta.get_field('sales_account')
<django.db.models.fields.related.ForeignKey: sales_account>
The sales journal¶
The cosi2 demo site has no VAT declarations, no purchase journals, no financial journals, just a single sales journal.
>>> rt.show('accounting.Journals', column_names="ref name trade_type")
=========== ================ ================== ============
Reference Designation Designation (en) Trade type
----------- ---------------- ------------------ ------------
SLS Factures vente Sales invoices Sales
=========== ================ ================== ============
Invoices are sorted by number and year. The entry date should normally never “go back”. Lino supports exceptional situations, e.g. starting to issue invoices at a given number and entering a series of sales invoices from a legacy system afterwards.
>>> jnl = rt.models.accounting.Journal.get_by_ref("SLS")
>>> rt.show('trading.InvoicesByJournal', jnl)
...
===================== ============ =========================== ============== =============== ================
No. Entry date Partner Subject line Total to pay Workflow
--------------------- ------------ --------------------------- -------------- --------------- ----------------
15/2017 12/03/2017 Bogaert Aabid 1 110,16 **Registered**
14/2017 11/03/2017 Bogaert Aabid 535,00 **Registered**
13/2017 10/03/2017 Boesmans Aabdeen 280,00 **Registered**
...
3/2017 08/02/2017 Blaas Léona 719,60 **Registered**
2/2017 07/02/2017 Bietmé Rubens 645,00 **Registered**
1/2017 07/01/2017 Bertrand Louise 31,92 **Registered**
57/2016 10/12/2016 Bernard Oscar 3 149,71 **Registered**
56/2016 09/12/2016 Beckers Joséphine 1 613,92 **Registered**
55/2016 08/12/2016 Beck Max 448,50 **Registered**
...
6/2016 07/02/2016 Donderweer BV 1 110,16 **Registered**
5/2016 11/01/2016 Garage Mergelsberg 535,00 **Registered**
4/2016 10/01/2016 Bäckerei Schmitz 280,00 **Registered**
3/2016 09/01/2016 Bäckerei Mießen 679,81 **Registered**
2/2016 08/01/2016 Bäckerei Ausdemwald 2 039,82 **Registered**
1/2016 07/01/2016 Rumma & Ko OÜ 2 999,85 **Registered**
**Total (72 rows)** **82 597,39**
===================== ============ =========================== ============== =============== ================
>>> mt = contenttypes.ContentType.objects.get_for_model(accounting.Journal).id
>>> obj = trading.VatProductInvoice.objects.get(journal__ref="SLS", number=20)
>>> url = '/api/trading/InvoicesByJournal/{0}'.format(obj.id)
>>> url += '?mt={0}&mk={1}&an=detail&fmt=json'.format(mt, obj.journal.id)
>>> test_client.force_login(rt.login('robin').user)
>>> res = test_client.get(url, REMOTE_USER='robin')
>>> # res.content
>>> r = check_json_result(res, "navinfo data disable_delete id param_values title")
>>> print(r['title'])
<a ...>Sales invoices (SLS)</a> » SLS 20/2016
IllegalText: The <text:section> element does not allow text¶
The following reproduces a situation which caused above error until 2015-11-11.
TODO: it is currently disabled for different reasons: leaves dangling temporary directories, does not reproduce the problem (probably because we must clear the cache).
>> obj = rt.models.trading.VatProductInvoice.objects.all()[0] >> obj VatProductInvoice #1 (‘SLS#1’) >> from lino.modlib.appypod.appy_renderer import AppyRenderer >> tplfile = rt.find_config_file(‘trading/VatProductInvoice/Default.odt’) >> context = dict() >> outfile = “tmp.odt” >> renderer = AppyRenderer(ses, tplfile, context, outfile) >> ar = rt.models.trading.ItemsByInvoicePrint.request(obj) >> print(renderer.insert_table(ar)) #doctest: +ELLIPSIS <table:table …</table:table-rows></table:table>
>> item = obj.items.all()[0] >> item.description = “”” … <p>intro:</p><ol><li>first</li><li>second</li></ol> … <p></p> … “”” >> item.save() >> print(renderer.insert_table(ar)) #doctest: +ELLIPSIS Traceback (most recent call last): … IllegalText: The <text:section> element does not allow text
The language of an invoice¶
The language of an invoice not necessary that of the user who enters
the invoice. It is either the partner’s language
or (if this is empty)
the Site’s get_default_language
.
Some fields of a registered voucher can remain editable¶
The default behaviour is that a registered voucher is not editable.
>>> UserTypes = rt.models.users.UserTypes
>>> InvoicesByJournal = rt.models.trading.InvoicesByJournal
>>> obj = InvoicesByJournal.model.objects.first()
>>> obj.state
<accounting.VoucherStates.registered:20>
>>> ar = rt.login("robin")
>>> actor = InvoicesByJournal
>>> actor.get_row_permission(
... obj, ar, actor.get_row_state(obj), actor.update_action)
False
But if you set accounting.VoucherState.is_editable to True for the
registered
state, then the record itself becomes editable.
>>> accounting.VoucherStates.registered.is_editable = True
>>> actor.get_row_permission(
... obj, ar, actor.get_row_state(obj), actor.update_action)
True
>>> obj.disabled_fields(obj.get_default_table().request(parent=ar))
{'clear_printed'}
Only the voucher state refuses editing, the actors don’t disable editing for all rows:
>>> rt.models.trading.InvoicesByJournal.editable
True
>>> InvoicesByJournal.hide_editing(UserTypes.admin)
False
TODO: Split is_editable of a voucher state into two booleans: fields_editable and row_editable. For example the partner field of a registered trading invoice must never be editable while the language field or some narration might remain editable.