diff --git a/MANIFEST.in b/MANIFEST.in index 1489ca2..4c6b589 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -9,4 +9,4 @@ include view/*.xml include locale/*.po include doc/* include tests/*.rst -include *.odt +include *.fodt diff --git a/__init__.py b/__init__.py index 497aecd..713e62f 100644 --- a/__init__.py +++ b/__init__.py @@ -1,10 +1,10 @@ # The COPYRIGHT file at the top level of this repository contains the full # copyright notices and license terms. from trytond.pool import Pool -from .invoice import * +from . import invoice def register(): Pool.register( - InvoiceLine, + invoice.InvoiceLine, module='account_invoice_discount', type_='model') diff --git a/invoice.fodt b/invoice.fodt new file mode 100644 index 0000000..2e6bfec --- /dev/null +++ b/invoice.fodt @@ -0,0 +1,1157 @@ + + + + LibreOffice/5.4.2.2.0$Linux_X86_64 LibreOffice_project/40m0$Build-22012-11-15T15:00:351P0D + + + 55541 + 0 + 25049 + 21408 + true + false + + + view2 + 3729 + 79347 + 0 + 55541 + 25047 + 76948 + 0 + 1 + false + 100 + false + false + + + + + false + true + true + true + 0 + true + true + + false + false + false + false + false + false + false + false + false + false + false + false + false + true + false + true + true + false + false + false + false + + false + false + false + true + false + false + + false + false + false + false + true + 2578396 + false + true + false + false + true + true + false + true + 0 + false + true + high-resolution + false + false + false + true + true + true + + true + false + false + true + false + false + false + + false + false + 882034 + false + 1 + true + false + false + 0 + false + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <if test="invoice.company.header"> + <for each="line in invoice.company.header.split('\n')"> + <line> + </for> + </if> + <invoice.company.rec_name> + + + <if test="invoice.company.footer"> + <for each="line in invoice.company.footer.split('\n')"> + <line> + </for> + </if> + + + + + + + + + + + + + <replace text:p="set_lang(invoice.party.lang and invoice.party.lang.code or 'en')"> + <replace text:p="invoice.set_lang(invoice.party.lang and invoice.party.lang.code or 'en')"> + <for each="line in invoice.invoice_address.full_address.split('\n')"> + <line> + </for> + <if test="invoice.party_tax_identifier"> + <invoice.party_tax_identifier.type_string>: <invoice.party_tax_identifier.code> + </if> + <if test="invoice.type == 'in'"> + Supplier Invoice N°:<invoice.number and ' ' + invoice.number or ''> + </if> + <if test="invoice.type == 'out'"> + <choose test=""> + <when test="invoice.state == 'draft'"> + Draft Invoice + </when> + <when test="invoice.state == 'validated'"> + Pro forma Invoice + </when> + <otherwise test=""> + Invoice N°:<invoice.number and ' ' + invoice.number or ''> + </otherwise> + </choose> + </if> + Description: <invoice.description or ''> + Reference: <invoice.origins or ''><', ' if (invoice.reference and invoice.origins) else ''><invoice.reference or ''> + <if test="invoice.invoice_date"> + Date: <format_date(invoice.invoice_date, invoice.party.lang)> + </if> + <if test="invoice.tax_identifier"> + <invoice.tax_identifier.type_string>: <invoice.tax_identifier.code> + </if> + + + + + + + + + + + + + Description + + + Quantity + + + + Unit Price + + + Discount + + + Taxes + + + Amount + + + + + + <for each="line in invoice.lines"> + + + + + + + + + + + + + <choose test=""> + + + + + + + + + + + + + <when test="line.type == 'line'"> + + + + + + + + + + + + + <for each="line in line.description.split('\n')"> + <line> + </for> + + + <(format_number(line.quantity, invoice.party.lang, digits=line.unit_digits) + (line.unit and (' ' + line.unit.symbol) or '')) or ''> + + + + <format_currency(line.unit_price, invoice.party.lang, invoice.currency)> + + + <format_number(line.discount * 100, invoice.party.lang)>% + + + <','.join('[' + str(x.sequence_number) + ']' for x in line.invoice_taxes)> + + + <format_currency(line.amount, invoice.party.lang, invoice.currency)> + + + + + + + + + + </when> + + + + + + + + + <when test="line.type == 'subtotal'"> + + + + + + + + + + + + + <for each="line in line.description.split('\n')"> + <line> + </for> + + + + + + + + + + <format_currency(line.amount, invoice.party.lang, invoice.currency)> + + + + + </when> + + + + + + + + + + + + + <when test="line.type == 'title'"> + + + + + + + + + + + + + <for each="line in line.description.split('\n')"> + <line> + </for> + + + + + + + + + + + + + </when> + + + + + + + + + + + + + <otherwise test=""> + + + + + + + + + + + + + <for each="line in line.description.split('\n')"> + <line> + </for> + + + + + + + + + + + + + </otherwise> + + + + + + + + + + + + + </choose> + + + + + + + + + + + + + </for> + + + + + + + + + + + + + + + + + + + + + + + + Tax + + + Base + + + Amount + + + + + + <for each="tax in invoice.taxes"> + + + + + + + <'[' + '%s' % tax.sequence_number + ']'><tax.description or ''> + + + <format_currency(tax.base, invoice.party.lang, invoice.currency)> + + + <format_currency(tax.amount, invoice.party.lang, invoice.currency)> + + + + + <if test="tax.legal_notice"> + + + + + + + <for each="line in tax.legal_notice.split('\n')"> + <line> + </for> + + + + + + + </if> + + + + + + + </for> + + + + + + + + + + + + + + Total (excl. taxes): + + + <format_currency(invoice.untaxed_amount, invoice.party.lang, invoice.currency)> + + + + + Taxes: + + + <format_currency(invoice.tax_amount, invoice.party.lang, invoice.currency)> + + + + + Total: + + + <format_currency(invoice.total_amount, invoice.party.lang, invoice.currency)> + + + + + + + + <if test="invoice.lines_to_pay"> + + + + + + + + Payment Term + <if test="invoice.payment_term and invoice.payment_term.description"> + <for each="description in (invoice.payment_term.description or '').split('\n')"> + <description> + </for> + </if> + + + + + + Date + + + Amount + + + + + + <for each="line in invoice.lines_to_pay"> + + + + + + <format_date(line.maturity_date, invoice.party.lang)> + + + <line.amount_second_currency and format_currency(line.amount_second_currency, invoice.party.lang, invoice.currency) or format_currency(line.debit - line.credit, invoice.party.lang, invoice.currency)> + + + + + </for> + + + + + </if> + <for each="comment in (invoice.comment or '').split('\n')"> + <comment> + </for> + + + \ No newline at end of file diff --git a/invoice.odt b/invoice.odt deleted file mode 100644 index b2a8457..0000000 Binary files a/invoice.odt and /dev/null differ diff --git a/invoice.py b/invoice.py index 578bf28..06a8e73 100644 --- a/invoice.py +++ b/invoice.py @@ -33,13 +33,9 @@ class InvoiceLine: super(InvoiceLine, cls).__setup__() cls.unit_price.states['readonly'] = True cls.unit_price.digits = (20, price_digits[1] + discount_digits[1]) - if 'discount' not in cls.amount.on_change_with: - cls.amount.on_change_with.add('discount') - if 'gross_unit_price' not in cls.amount.on_change_with: - cls.amount.on_change_with.add('gross_unit_price') - @staticmethod - def default_discount(): + @classmethod + def default_discount(cls): return Decimal(0) def update_prices(self): @@ -78,6 +74,10 @@ class InvoiceLine: def on_change_discount(self): return self.update_prices() + @fields.depends('discount', 'gross_unit_price') + def on_change_with_amount(self): + return super(InvoiceLine, self).on_change_with_amount() + @fields.depends('gross_unit_price', 'unit_price', 'discount') def on_change_product(self): super(InvoiceLine, self).on_change_product() diff --git a/invoice.xml b/invoice.xml index 0a2a606..cbd1269 100644 --- a/invoice.xml +++ b/invoice.xml @@ -15,10 +15,20 @@ + + + + Invoice account.invoice account.invoice - account_invoice_discount/invoice.odt + account_invoice_discount/invoice.fodt + + + + form_print + account.invoice,-1 + diff --git a/setup.py b/setup.py index 1688a51..3c5c1ca 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ setup(name=name, ], package_data={ 'trytond.modules.account_invoice_discount': (info.get('xml', []) - + ['tryton.cfg', 'view/*.xml', 'locale/*.po', '*.odt', + + ['tryton.cfg', 'view/*.xml', 'locale/*.po', '*.fodt', 'icons/*.svg', 'tests/*.rst']), }, classifiers=[ diff --git a/tests/scenario_invoice.rst b/tests/scenario_invoice.rst index 86c616b..67e067b 100644 --- a/tests/scenario_invoice.rst +++ b/tests/scenario_invoice.rst @@ -63,20 +63,16 @@ Create product:: >>> ProductUom = Model.get('product.uom') >>> unit, = ProductUom.find([('name', '=', 'Unit')]) >>> ProductTemplate = Model.get('product.template') - >>> Product = Model.get('product.product') - >>> product = Product() >>> template = ProductTemplate() >>> template.name = 'product' >>> template.default_uom = unit >>> template.type = 'service' >>> template.list_price = Decimal('20') - >>> template.cost_price = Decimal('12') >>> template.account_expense = expense >>> template.account_revenue = revenue >>> template.customer_taxes.append(tax) >>> template.save() - >>> product.template = template - >>> product.save() + >>> product, = template.products Create payment term:: @@ -179,7 +175,7 @@ Post invoice and check again invoice totals and taxes:: >>> credit_note_tax_code.sum Decimal('0.00') -Discounts are copyied when crediting the invoice:: +Discounts are copied when crediting the invoice:: >>> credit = Wizard('account.invoice.credit', [invoice]) >>> credit.form.with_refund = True diff --git a/tox.ini b/tox.ini index 08e43cf..527d859 100644 --- a/tox.ini +++ b/tox.ini @@ -1,10 +1,10 @@ [tox] -envlist = {py27,py33,py34,py35}-{sqlite,postgresql,mysql},pypy-{sqlite,postgresql} +envlist = {py27,py34,py35,py36}-{sqlite,postgresql,mysql},pypy-{sqlite,postgresql} [testenv] commands = {envpython} setup.py test deps = - {py27,py33,py34,py35}-postgresql: psycopg2 >= 2.5 + {py27,py34,py35,py36}-postgresql: psycopg2 >= 2.5 pypy-postgresql: psycopg2cffi >= 2.5 mysql: MySQL-python sqlite: sqlitebck diff --git a/tryton.cfg b/tryton.cfg index b044ae0..9f91b73 100644 --- a/tryton.cfg +++ b/tryton.cfg @@ -1,5 +1,5 @@ [tryton] -version=4.3.0 +version=4.7.0 depends: ir account_invoice