changeset 898b35dc3d46 in modules/account_es:default
details: 
https://hg.tryton.org/modules/account_es?cmd=changeset&node=898b35dc3d46
description:
        Add VAT Book report

        issue8701
        review310091002
diffstat:

 CHANGELOG                                       |    2 +
 __init__.py                                     |    4 +
 account.py                                      |   43 +++-
 party.py                                        |    9 +
 reporting_tax.py                                |  265 +++++++++++++++++++++++-
 reporting_tax.xml                               |   54 ++++
 tax.xml                                         |    3 +
 tax_normal.xml                                  |    3 +
 tax_pyme.xml                                    |    3 +
 tests/scenario_reporting.rst                    |   24 ++
 tests/scenario_reporting_alternate_currency.rst |   14 +
 tests/scenario_reporting_surcharge_tax.rst      |  135 ++++++++++++
 tests/test_account_es.py                        |    5 +
 tests/vat_book.csv                              |    2 +
 view/tax_form.xml                               |    2 +
 view/tax_template_form.xml                      |    2 +
 view/vat_book_context_form.xml                  |   15 +
 view/vat_book_list.xml                          |   14 +
 18 files changed, 597 insertions(+), 2 deletions(-)

diffs (827 lines):

diff -r 2007d9a3de34 -r 898b35dc3d46 CHANGELOG
--- a/CHANGELOG Sun Apr 11 20:52:06 2021 +0200
+++ b/CHANGELOG Mon Apr 12 09:26:14 2021 +0200
@@ -1,3 +1,5 @@
+* Add VAT book report
+
 Version 5.8.0 - 2020-11-02
 * Bug fixes (see mercurial logs for details)
 * Report credit note on modification tax codes
diff -r 2007d9a3de34 -r 898b35dc3d46 __init__.py
--- a/__init__.py       Sun Apr 11 20:52:06 2021 +0200
+++ b/__init__.py       Mon Apr 12 09:26:14 2021 +0200
@@ -14,12 +14,15 @@
         account.TaxCode,
         account.TaxTemplate,
         account.Tax,
+        account.Invoice,
         party.Party,
         party.Identifier,
         reporting_tax.ESVATList,
         reporting_tax.ESVATListContext,
         reporting_tax.ECOperationList,
         reporting_tax.ECOperationListContext,
+        reporting_tax.ESVATBook,
+        reporting_tax.ESVATBookContext,
         module='account_es', type_='model')
     Pool.register(
         reporting_tax.AEAT111,
@@ -27,6 +30,7 @@
         reporting_tax.AEAT303,
         reporting_tax.AEAT347,
         reporting_tax.AEAT349,
+        reporting_tax.VATBookReport,
         module='account_es', type_='report')
     Pool.register(
         reporting_tax.PrintAEAT,
diff -r 2007d9a3de34 -r 898b35dc3d46 account.py
--- a/account.py        Sun Apr 11 20:52:06 2021 +0200
+++ b/account.py        Mon Apr 12 09:26:14 2021 +0200
@@ -3,6 +3,7 @@
 from trytond.model import fields
 from trytond.pool import PoolMeta, Pool
 from trytond.pyson import Bool, Eval
+from trytond.transaction import Transaction
 
 
 class TaxCodeTemplate(metaclass=PoolMeta):
@@ -43,10 +44,17 @@
     es_vat_list_code = fields.Char("Spanish VAT List Code")
     es_ec_purchases_list_code = fields.Char("Spanish EC Purchase List Code")
     es_reported_with = fields.Many2One('account.tax.template', "Reported With")
+    es_exclude_from_vat_book = fields.Boolean("Exclude from Spanish VAT Book")
+
+    @classmethod
+    def default_es_exclude_from_vat_book(cls):
+        return False
 
     def _get_tax_value(self, tax=None):
         value = super()._get_tax_value(tax=tax)
-        for name in ['es_vat_list_code', 'es_ec_purchases_list_code']:
+        for name in [
+                'es_vat_list_code', 'es_ec_purchases_list_code',
+                'es_exclude_from_vat_book']:
             if not tax or getattr(tax, name) != getattr(self, name):
                 value[name] = getattr(self, name)
         return value
@@ -96,6 +104,16 @@
                 & ~Eval('template_override', False)),
             },
         depends=['template', 'template_override'])
+    es_exclude_from_vat_book = fields.Boolean("Exclude from Spanish VAT Book",
+        states={
+            'readonly': (Bool(Eval('template', -1))
+                & ~Eval('template_override', False)),
+            },
+        depends=['template', 'template_override'])
+
+    @classmethod
+    def default_es_exclude_from_vat_book(cls):
+        return False
 
     @classmethod
     def update_tax(cls, company_id, template2account, template2tax=None):
@@ -123,3 +141,26 @@
 
         if to_write:
             cls.write(*to_write)
+
+
+class Invoice(metaclass=PoolMeta):
+    __name__ = 'account.invoice'
+
+    @property
+    def es_vat_book_type(self):
+        if 'credit_note' in self._sequence_field:
+            return 'R0'
+        if not self.party_tax_identifier:
+            return 'F2'
+        return 'F1'
+
+    @property
+    def es_vat_book_serie(self):
+        return ''
+
+    @property
+    def es_vat_book_number(self):
+        vat_book_type = Transaction().context.get('es_vat_book_type', 'E')
+        if vat_book_type != 'E' and self.reference:
+            return self.reference
+        return self.number
diff -r 2007d9a3de34 -r 898b35dc3d46 party.py
--- a/party.py  Sun Apr 11 20:52:06 2021 +0200
+++ b/party.py  Mon Apr 12 09:26:14 2021 +0200
@@ -34,3 +34,12 @@
         if self.type == 'eu_vat':
             return self.code[2:]
         return self.code
+
+    def es_vat_type(self):
+        country = self.es_country()
+        if country == 'ES':
+            return ''
+        type_ = '02'
+        if country is None:
+            type_ = '06'
+        return type_
diff -r 2007d9a3de34 -r 898b35dc3d46 reporting_tax.py
--- a/reporting_tax.py  Sun Apr 11 20:52:06 2021 +0200
+++ b/reporting_tax.py  Mon Apr 12 09:26:14 2021 +0200
@@ -1,6 +1,8 @@
 # This file is part of Tryton.  The COPYRIGHT file at the top level of
 # this repository contains the full copyright notices and license terms.
+import csv
 import unicodedata
+from io import StringIO
 
 from collections import defaultdict
 from dateutil.relativedelta import relativedelta
@@ -9,7 +11,7 @@
 
 from sql import Cast, Null, Literal
 from sql.aggregate import Count, Min, Sum
-from sql.conditionals import Case
+from sql.conditionals import Case, Coalesce
 from sql.functions import Substring, Position, Extract, CurrentTimestamp
 from sql.operators import Exists
 
@@ -708,3 +710,264 @@
         context['format_decimal'] = format_decimal
 
         return context
+
+
+class ESVATBookContext(ModelView):
+    "Spanish VAT Book Context"
+    __name__ = 'account.reporting.vat_book_es.context'
+
+    company = fields.Many2One('company.company', "Company", required=True)
+    fiscalyear = fields.Many2One('account.fiscalyear', "Fiscal Year",
+        required=True,
+        domain=[
+            ('company', '=', Eval('company')),
+            ],
+        depends=['company'])
+    start_period = fields.Many2One('account.period', "Start Period",
+        domain=[
+            ('fiscalyear', '=', Eval('fiscalyear')),
+            ('start_date', '<=', (Eval('end_period'), 'start_date')),
+            ], depends=['fiscalyear', 'end_period'])
+    end_period = fields.Many2One('account.period', "End Period",
+        domain=[
+            ('fiscalyear', '=', Eval('fiscalyear')),
+            ('start_date', '>=', (Eval('start_period'), 'start_date'))
+            ],
+        depends=['fiscalyear', 'start_period'])
+    es_vat_book_type = fields.Selection([
+            # Use same key as tax authority
+            ('E', "Issued"),
+            ('R', "Received"),
+            ('S', "Investment Goods"),
+            ],
+        "Type", required=True)
+
+    @classmethod
+    def default_es_vat_book_type(cls):
+        return 'E'
+
+    @classmethod
+    def default_company(cls):
+        return Transaction().context.get('company')
+
+    @classmethod
+    def default_fiscalyear(cls):
+        pool = Pool()
+        Fiscalyear = pool.get('account.fiscalyear')
+        return Fiscalyear.find(cls.default_company(), exception=False)
+
+
+class ESVATBook(ModelSQL, ModelView):
+    "Spanish VAT Book"
+    __name__ = 'account.reporting.vat_book_es'
+
+    invoice = fields.Many2One('account.invoice', "Invoice")
+    invoice_date = fields.Date("Invoice Date")
+    party = fields.Many2One('party.party', "Party")
+    party_tax_identifier = fields.Many2One(
+        'party.identifier', "Party Tax Identifier")
+    tax = fields.Many2One('account.tax', "Tax")
+    base_amount = fields.Numeric("Base Amount",
+        digits=(16, Eval('currency_digits', 2)),
+        depends=['currency_digits'])
+    tax_amount = fields.Numeric("Tax Amount",
+        digits=(16, Eval('currency_digits', 2)),
+        depends=['currency_digits'])
+    surcharge_tax = fields.Many2One('account.tax', "Surcharge Tax")
+    surcharge_tax_amount = fields.Numeric("Surcharge Tax Amount",
+        digits=(16, Eval('currency_digits', 2)),
+        depends=['currency_digits'])
+    currency_digits = fields.Function(fields.Integer("Currency Digits"),
+        'get_currency_digits')
+
+    @classmethod
+    def included_tax_groups(cls):
+        pool = Pool()
+        ModelData = pool.get('ir.model.data')
+        tax_groups = []
+        vat_book_type = Transaction().context.get('es_vat_book_type')
+        if vat_book_type == 'E':
+            tax_groups.append(ModelData.get_id(
+                    'account_es', 'tax_group_sale'))
+            tax_groups.append(ModelData.get_id(
+                    'account_es', 'tax_group_sale_service'))
+        elif vat_book_type == 'R':
+            tax_groups.append(ModelData.get_id(
+                    'account_es', 'tax_group_purchase'))
+            tax_groups.append(ModelData.get_id(
+                    'account_es', 'tax_group_purchase_service'))
+        elif vat_book_type == 'S':
+            tax_groups.append(ModelData.get_id(
+                    'account_es', 'tax_group_purchase_investment'))
+        return tax_groups
+
+    @classmethod
+    def table_query(cls):
+        pool = Pool()
+        Company = pool.get('company.company')
+        Invoice = pool.get('account.invoice')
+        Move = pool.get('account.move')
+        Line = pool.get('account.move.line')
+        TaxLine = pool.get('account.tax.line')
+        Period = pool.get('account.period')
+        Tax = pool.get('account.tax')
+        context = Transaction().context
+        company = Company.__table__()
+        invoice = Invoice.__table__()
+        move = Move.__table__()
+        line = Line.__table__()
+        tax_line = TaxLine.__table__()
+        period = Period.__table__()
+        tax = Tax.__table__()
+
+        where = ((invoice.company == context.get('company'))
+            & (period.fiscalyear == context.get('fiscalyear'))
+            & ~tax.es_exclude_from_vat_book)
+        groups = cls.included_tax_groups()
+        if groups:
+            where &= tax.group.in_(groups)
+        if context.get('start_period'):
+            start_period = Period(context['start_period'])
+            where &= (period.start_date >= start_period.start_date)
+        if context.get('end_period'):
+            end_period = Period(context['end_period'])
+            where &= (period.end_date <= end_period.end_date)
+
+        query = (tax_line
+            .join(tax, condition=tax_line.tax == tax.id)
+            .join(line, condition=tax_line.move_line == line.id)
+            .join(move, condition=line.move == move.id)
+            .join(period, condition=move.period == period.id)
+            .join(invoice, condition=invoice.move == move.id)
+            .join(company, condition=company.id == invoice.company)
+            .select(
+                Min(tax_line.id).as_('id'),
+                Literal(0).as_('create_uid'),
+                CurrentTimestamp().as_('create_date'),
+                cls.write_uid.sql_cast(Literal(Null)).as_('write_uid'),
+                cls.write_date.sql_cast(Literal(Null)).as_('write_date'),
+                invoice.id.as_('invoice'),
+                invoice.invoice_date.as_('invoice_date'),
+                invoice.party.as_('party'),
+                invoice.party_tax_identifier.as_('party_tax_identifier'),
+                Coalesce(tax.es_reported_with, tax.id).as_('tax'),
+                Sum(tax_line.amount,
+                    filter_=((tax_line.type == 'base')
+                        & (tax.es_reported_with == Null))).as_('base_amount'),
+                Coalesce(
+                    Sum(tax_line.amount,
+                        filter_=((tax_line.type == 'tax')
+                            & (tax.es_reported_with == Null))),
+                    0).as_('tax_amount'),
+                Min(tax.id,
+                    filter_=(tax.es_reported_with != Null)).as_(
+                    'surcharge_tax'),
+                Sum(tax_line.amount,
+                    filter_=((tax_line.type == 'tax')
+                        & (tax.es_reported_with != Null))).as_(
+                    'surcharge_tax_amount'),
+                where=where,
+                group_by=[
+                    invoice.id,
+                    invoice.party,
+                    invoice.invoice_date,
+                    invoice.party_tax_identifier,
+                    Coalesce(tax.es_reported_with, tax.id),
+                    ]))
+        return query
+
+    def get_currency_digits(self, name):
+        return self.invoice.company.currency.digits
+
+
+class VATBookReport(Report):
+    __name__ = 'account.reporting.aeat.vat_book'
+
+    @classmethod
+    def get_context(cls, records, header, data):
+        context = super().get_context(records, header, data)
+
+        context['format_decimal'] = cls.format_decimal
+        context['get_period'] = cls.get_period
+        return context
+
+    @classmethod
+    def render(cls, report, report_context):
+        return cls.render_csv(report, report_context)
+
+    @classmethod
+    def convert(cls, report, data, **kwargs):
+        output_format = report.extension or report.template_extension
+        if not report.report_content and output_format == 'csv':
+            return output_format, data
+        return super().convert(report, data, **kwargs)
+
+    @classmethod
+    def get_period(cls, date):
+        return str((date.month + 2) // 3) + 'T'
+
+    @classmethod
+    def format_decimal(cls, n):
+        if not isinstance(n, Decimal):
+            n = Decimal(n)
+        sign = '-' if n < 0 else ''
+        return sign + '{0:.2f}'.format(abs(n)).replace('.', ',')
+
+    @classmethod
+    def get_format_date(cls):
+        pool = Pool()
+        Lang = pool.get('ir.lang')
+        es = Lang(code='es', date='%d/%m/%Y')
+        return lambda value: es.strftime(value, '%d/%m/%Y')
+
+    @classmethod
+    def render_csv(cls, report, report_context):
+        vat_book = StringIO()
+        writer = csv.writer(
+            vat_book, delimiter=';', doublequote=False, escapechar='\\',
+            quoting=csv.QUOTE_NONE)
+        for record in report_context['records']:
+            writer.writerow(cls.get_row(record, report_context))
+        value = vat_book.getvalue()
+        if not isinstance(value, bytes):
+            value = value.encode('utf-8')
+        return value
+
+    @classmethod
+    def get_row(cls, record, report_context):
+        context = Transaction().context
+        format_date = cls.get_format_date()
+        return [
+            record.invoice_date.year,
+            report_context['get_period'](record.invoice_date),
+            context['es_vat_book_type'],
+            '',
+            record.invoice.es_vat_book_type,
+            '',
+            '',
+            format_date(record.invoice_date),
+            '',
+            record.invoice.es_vat_book_serie,
+            record.invoice.es_vat_book_number,
+            (record.party_tax_identifier.es_vat_type()
+                if record.party_tax_identifier else ''),
+            (record.party_tax_identifier.es_code()
+                if record.party_tax_identifier else ''),
+            country_code(record),
+            record.party.name[:40],
+            '',
+            cls.format_decimal(record.invoice.total_amount),
+            cls.format_decimal(record.base_amount),
+            cls.format_decimal(record.tax.rate * 100),
+            cls.format_decimal(record.tax_amount),
+            (cls.format_decimal(record.surcharge_tax.rate * 100)
+                if record.surcharge_tax else ''),
+            (cls.format_decimal(record.surcharge_tax_amount)
+                if record.surcharge_tax else ''),
+            '',
+            '',
+            '',
+            '',
+            '',
+            '',
+            ]
diff -r 2007d9a3de34 -r 898b35dc3d46 reporting_tax.xml
--- a/reporting_tax.xml Sun Apr 11 20:52:06 2021 +0200
+++ b/reporting_tax.xml Mon Apr 12 09:26:14 2021 +0200
@@ -187,5 +187,59 @@
             <field 
name="model">account.reporting.es_ec_operation_list,-1</field>
             <field name="action" ref="report_aeat_349"/>
         </record>
+
+        <record model="ir.ui.view" id="vat_book_context_view_form">
+            <field name="model">account.reporting.vat_book_es.context</field>
+            <field name="type">form</field>
+            <field name="name">vat_book_context_form</field>
+        </record>
+        <record model="ir.ui.view" id="vat_book_view_list">
+            <field name="model">account.reporting.vat_book_es</field>
+            <field name="type">tree</field>
+            <field name="name">vat_book_list</field>
+        </record>
+        <record model="ir.action.act_window" id="act_vat_book_list">
+            <field name="name">Spanish VAT Book</field>
+            <field name="res_model">account.reporting.vat_book_es</field>
+            <field 
name="context_model">account.reporting.vat_book_es.context</field>
+        </record>
+        <record model="ir.action.act_window.view" id="act_vat_book_list_view1">
+            <field name="sequence" eval="10" />
+            <field name="view" ref="vat_book_view_list" />
+            <field name="act_window" ref="act_vat_book_list" />
+        </record>
+        <menuitem parent="account.menu_reporting" action="act_vat_book_list" 
id="menu_vat_book" />
+
+        <record model="ir.model.access" id="access_vat_book">
+            <field name="model" search="[('model', '=', 
'account.reporting.vat_book_es')]"/>
+            <field name="perm_read" eval="False"/>
+            <field name="perm_write" eval="False"/>
+            <field name="perm_create" eval="False"/>
+            <field name="perm_delete" eval="False"/>
+        </record>
+        <record model="ir.model.access" id="access_vat_book_account">
+            <field name="model" search="[('model', '=', 
'account.reporting.vat_book_es')]"/>
+            <field name="group" ref="account.group_account"/>
+            <field name="perm_read" eval="True"/>
+            <field name="perm_write" eval="False"/>
+            <field name="perm_create" eval="False"/>
+            <field name="perm_delete" eval="False"/>
+        </record>
+
+        <record model="ir.action.report" id="report_aeat_vat_book">
+            <field name="name">VAT Book</field>
+            <field name="records">listed</field>
+            <field name="report_name">account.reporting.aeat.vat_book</field>
+            <field name="model">account.reporting.vat_book_es</field>
+            <field name="report"></field>
+            <field name="template_extension">txt</field>
+            <field name="extension">csv</field>
+            <field name="translatable" eval="False"/>
+        </record>
+        <record model="ir.action.keyword" id="report_aeat_vat_book_keyword">
+            <field name="keyword">form_print</field>
+            <field name="model">account.reporting.vat_book_es,-1</field>
+            <field name="action" ref="report_aeat_vat_book"/>
+        </record>
     </data>
 </tryton>
diff -r 2007d9a3de34 -r 898b35dc3d46 tax.xml
--- a/tax.xml   Sun Apr 11 20:52:06 2021 +0200
+++ b/tax.xml   Mon Apr 12 09:26:14 2021 +0200
@@ -468,6 +468,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477"/>
             <field name="credit_note_account" ref="pgc_477"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record model="account.tax.template" 
id="iva_sop_intracomunitario_servicios_1">
             <field name="name">IVA Intracomunitario. Servicios (1)</field>
@@ -491,6 +492,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477"/>
             <field name="credit_note_account" ref="pgc_477"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record model="account.tax.template" 
id="iva_sop_intracomunitario_inv_1">
             <field name="name">IVA Intracomunitario. Bienes inversión 
(1)</field>
@@ -514,6 +516,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477"/>
             <field name="credit_note_account" ref="pgc_477"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record model="account.tax.code.template" id="vat_code_iva">
             <field name="name">Impuesto sobre el Valor Añadido - 
Autoliquidación</field>
diff -r 2007d9a3de34 -r 898b35dc3d46 tax_normal.xml
--- a/tax_normal.xml    Sun Apr 11 20:52:06 2021 +0200
+++ b/tax_normal.xml    Mon Apr 12 09:26:14 2021 +0200
@@ -468,6 +468,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_normal"/>
             <field name="credit_note_account" ref="pgc_477_normal"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="iva_sop_intracomunitario_servicios_1_normal" 
model="account.tax.template">
             <field name="name">IVA Intracomunitario. Servicios (1)</field>
@@ -491,6 +492,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_normal"/>
             <field name="credit_note_account" ref="pgc_477_normal"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="iva_sop_intracomunitario_inv_1_normal" 
model="account.tax.template">
             <field name="name">IVA Intracomunitario. Bienes inversión 
(1)</field>
@@ -514,6 +516,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_normal"/>
             <field name="credit_note_account" ref="pgc_477_normal"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="vat_code_iva_normal" model="account.tax.code.template">
             <field name="name">Impuesto sobre el Valor Añadido - 
Autoliquidación</field>
diff -r 2007d9a3de34 -r 898b35dc3d46 tax_pyme.xml
--- a/tax_pyme.xml      Sun Apr 11 20:52:06 2021 +0200
+++ b/tax_pyme.xml      Mon Apr 12 09:26:14 2021 +0200
@@ -468,6 +468,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_pyme"/>
             <field name="credit_note_account" ref="pgc_477_pyme"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="iva_sop_intracomunitario_servicios_1_pyme" 
model="account.tax.template">
             <field name="name">IVA Intracomunitario. Servicios (1)</field>
@@ -491,6 +492,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_pyme"/>
             <field name="credit_note_account" ref="pgc_477_pyme"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="iva_sop_intracomunitario_inv_1_pyme" 
model="account.tax.template">
             <field name="name">IVA Intracomunitario. Bienes inversión 
(1)</field>
@@ -514,6 +516,7 @@
             <field name="rate" eval="Decimal('-0.21')"/>
             <field name="invoice_account" ref="pgc_477_pyme"/>
             <field name="credit_note_account" ref="pgc_477_pyme"/>
+            <field name="es_exclude_from_vat_book" eval="True"/>
         </record>
         <record id="vat_code_iva_pyme" model="account.tax.code.template">
             <field name="name">Impuesto sobre el Valor Añadido - 
Autoliquidación</field>
diff -r 2007d9a3de34 -r 898b35dc3d46 tests/scenario_reporting.rst
--- a/tests/scenario_reporting.rst      Sun Apr 11 20:52:06 2021 +0200
+++ b/tests/scenario_reporting.rst      Mon Apr 12 09:26:14 2021 +0200
@@ -234,3 +234,27 @@
     True
     >>> name.startswith('AEAT Model 349')
     True
+
+
+Only one tax of intracomunitary invoices is included on VAT Book::
+
+    >>> VatBook = Model.get('account.reporting.vat_book_es')
+    >>> context = {
+    ...     'company': company.id,
+    ...     'fiscalyear': fiscalyear.id,
+    ...     'es_vat_book_type': 'R',
+    ...     }
+    >>> with config.set_context(context):
+    ...     records = VatBook.find([])
+    >>> len(records)
+    2
+    >>> supplier_record, = [r for r in records if r.party == supplier]
+    >>> supplier_record.base_amount == Decimal('100.00')
+    True
+    >>> supplier_record.tax_amount == Decimal('21.00')
+    True
+    >>> ec_supplier_record, = [r for r in records if r.party == ec_supplier]
+    >>> ec_supplier_record.base_amount == Decimal('100.00')
+    True
+    >>> ec_supplier_record.tax_amount == Decimal('21.00')
+    True
diff -r 2007d9a3de34 -r 898b35dc3d46 
tests/scenario_reporting_alternate_currency.rst
--- a/tests/scenario_reporting_alternate_currency.rst   Sun Apr 11 20:52:06 
2021 +0200
+++ b/tests/scenario_reporting_alternate_currency.rst   Mon Apr 12 09:26:14 
2021 +0200
@@ -119,3 +119,17 @@
     True
     >>> record.amount == Decimal('50.00')
     True
+    >>> VatBook = Model.get('account.reporting.vat_book_es')
+    >>> context = {
+    ...     'company': company.id,
+    ...     'fiscalyear': fiscalyear.id,
+    ...     'es_vat_book_type': 'E',
+    ...     }
+    >>> with config.set_context(context):
+    ...     record, = VatBook.find([])
+    >>> record.party == party
+    True
+    >>> record.base_amount == Decimal('100.00')
+    True
+    >>> record.tax_amount == Decimal('21.00')
+    True
diff -r 2007d9a3de34 -r 898b35dc3d46 tests/scenario_reporting_surcharge_tax.rst
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/scenario_reporting_surcharge_tax.rst        Mon Apr 12 09:26:14 
2021 +0200
@@ -0,0 +1,135 @@
+===========================================
+Account ES Reporting Surcharge Tax Scenario
+===========================================
+
+Imports::
+
+    >>> import datetime
+    >>> from decimal import Decimal
+    >>> from proteus import Model, Wizard, Report
+    >>> from trytond.tests.tools import activate_modules
+    >>> from trytond.tools import file_open
+    >>> from trytond.modules.company.tests.tools import (
+    ...     create_company, get_company)
+    >>> from trytond.modules.account.tests.tools import (
+    ...     create_chart, get_accounts, create_fiscalyear)
+    >>> from trytond.modules.account_invoice.tests.tools import (
+    ...     set_fiscalyear_invoice_sequences)
+    >>> today = datetime.date.today()
+
+Activate modules::
+
+    >>> config = activate_modules('account_es')
+
+Create company::
+
+    >>> _ = create_company()
+    >>> company = get_company()
+    >>> tax_identifier = company.party.identifiers.new()
+    >>> tax_identifier.type = 'eu_vat'
+    >>> tax_identifier.code = 'ESB01000009'
+    >>> company.party.save()
+
+Create fiscal year::
+
+    >>> fiscalyear = set_fiscalyear_invoice_sequences(
+    ...     create_fiscalyear(company, datetime.date(2020, 1, 1)))
+    >>> fiscalyear.click('create_period')
+
+Create chart of accounts::
+
+    >>> _ = create_chart(company, 'account_es.pgc_0_pyme')
+    >>> accounts = get_accounts(company)
+    >>> expense = accounts['expense']
+    >>> revenue = accounts['revenue']
+
+Create parties::
+
+    >>> Party = Model.get('party.party')
+    >>> party = Party(name='Party')
+    >>> tax_identifier = party.identifiers.new()
+    >>> tax_identifier.type = 'eu_vat'
+    >>> tax_identifier.code = 'ES00000000T'
+    >>> party.save()
+    >>> surcharge_party = Party(name='Surcharge Party')
+    >>> tax_identifier = surcharge_party.identifiers.new()
+    >>> tax_identifier.type = 'eu_vat'
+    >>> tax_identifier.code = 'ES00000001R'
+    >>> surcharge_party.save()
+
+Create invoices::
+
+    >>> Tax = Model.get('account.tax')
+    >>> tax, = Tax.find([
+    ...     ('company', '=', company.id),
+    ...     ('group.kind', '=', 'sale'),
+    ...     ('name', '=', 'IVA 21% (bienes)'),
+    ...     ])
+    >>> surcharge_tax, = Tax.find([
+    ...     ('company', '=', company.id),
+    ...     ('group.kind', '=', 'sale'),
+    ...     ('es_reported_with', '=', tax.id),
+    ...     ])
+    >>> Invoice = Model.get('account.invoice')
+    >>> invoice = Invoice()
+    >>> invoice.party = party
+    >>> invoice.invoice_date = fiscalyear.start_date
+    >>> line = invoice.lines.new()
+    >>> line.account = revenue
+    >>> line.taxes.append(tax)
+    >>> line.quantity = 5
+    >>> line.unit_price = Decimal('20')
+    >>> invoice.click('post')
+    >>> invoice.click('post')
+    >>> invoice.total_amount
+    Decimal('121.00')
+    >>> invoice = Invoice()
+    >>> invoice.party = surcharge_party
+    >>> invoice.invoice_date = fiscalyear.start_date
+    >>> line = invoice.lines.new()
+    >>> line.account = revenue
+    >>> line.taxes.append(Tax(tax.id))
+    >>> line.taxes.append(surcharge_tax)
+    >>> line.quantity = 2
+    >>> line.unit_price = Decimal('25')
+    >>> invoice.click('post')
+    >>> invoice.total_amount
+    Decimal('63.10')
+
+Generate VAT Book::
+
+    >>> VatBook = Model.get('account.reporting.vat_book_es')
+    >>> context = {
+    ...     'company': company.id,
+    ...     'fiscalyear': fiscalyear.id,
+    ...     'es_vat_book_type': 'E',
+    ...     }
+    >>> with config.set_context(context):
+    ...     records = VatBook.find([])
+    ...     report = Report('account.reporting.aeat.vat_book')
+    ...     extension, content, _, name = report.execute(records)
+    >>> len(records)
+    2
+    >>> tax_record, = [r for r in records if not r.surcharge_tax]
+    >>> tax_record.party == party
+    True
+    >>> tax_record.base_amount == Decimal('100.00')
+    True
+    >>> tax_record.tax_amount == Decimal('21.00')
+    True
+    >>> surcharge_tax_record, = [r for r in records if r.surcharge_tax]
+    >>> surcharge_tax_record.party == surcharge_party
+    True
+    >>> surcharge_tax_record.base_amount == Decimal('50.00')
+    True
+    >>> surcharge_tax_record.tax_amount == Decimal('10.50')
+    True
+    >>> surcharge_tax_record.surcharge_tax_amount == Decimal('2.60')
+    True
+    >>> with file_open('account_es/tests/vat_book.csv', 'rb') as f:
+    ...     content == f.read()
+    True
+    >>> name.startswith('VAT Book')
+    True
+    >>> extension
+    'csv'
diff -r 2007d9a3de34 -r 898b35dc3d46 tests/test_account_es.py
--- a/tests/test_account_es.py  Sun Apr 11 20:52:06 2021 +0200
+++ b/tests/test_account_es.py  Mon Apr 12 09:26:14 2021 +0200
@@ -42,4 +42,9 @@
             tearDown=doctest_teardown, encoding='utf-8',
             checker=doctest_checker,
             optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
+    suite.addTests(doctest.DocFileSuite(
+            'scenario_reporting_surcharge_tax.rst',
+            tearDown=doctest_teardown, encoding='utf-8',
+            checker=doctest_checker,
+            optionflags=doctest.REPORT_ONLY_FIRST_FAILURE))
     return suite
diff -r 2007d9a3de34 -r 898b35dc3d46 tests/vat_book.csv
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/vat_book.csv        Mon Apr 12 09:26:14 2021 +0200
@@ -0,0 +1,2 @@
+2020;1T;E;;F1;;;01/01/2020;;;1;;00000000T;;Party;;121,00;100,00;21,00;21,00;;;;;;;;
+2020;1T;E;;F1;;;01/01/2020;;;2;;00000001R;;Surcharge 
Party;;63,10;50,00;21,00;10,50;5,20;2,60;;;;;;
diff -r 2007d9a3de34 -r 898b35dc3d46 view/tax_form.xml
--- a/view/tax_form.xml Sun Apr 11 20:52:06 2021 +0200
+++ b/view/tax_form.xml Mon Apr 12 09:26:14 2021 +0200
@@ -8,6 +8,8 @@
             <field name="es_vat_list_code"/>
             <label name="es_reported_with"/>
             <field name="es_reported_with"/>
+            <label name="es_exclude_from_vat_book"/>
+            <field name="es_exclude_from_vat_book"/>
         </page>
     </xpath>
     <xpath expr="/form//field[@name='ec_sales_list_code']" position="after">
diff -r 2007d9a3de34 -r 898b35dc3d46 view/tax_template_form.xml
--- a/view/tax_template_form.xml        Sun Apr 11 20:52:06 2021 +0200
+++ b/view/tax_template_form.xml        Mon Apr 12 09:26:14 2021 +0200
@@ -8,6 +8,8 @@
             <field name="es_vat_list_code"/>
             <label name="es_reported_with"/>
             <field name="es_reported_with"/>
+            <label name="es_exclude_from_vat_book"/>
+            <field name="es_exclude_from_vat_book"/>
         </page>
     </xpath>
     <xpath expr="/form//field[@name='ec_sales_list_code']" position="after">
diff -r 2007d9a3de34 -r 898b35dc3d46 view/vat_book_context_form.xml
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/view/vat_book_context_form.xml    Mon Apr 12 09:26:14 2021 +0200
@@ -0,0 +1,15 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<form>
+    <label name="fiscalyear"/>
+    <field name="fiscalyear"/>
+    <label name="company"/>
+    <field name="company"/>
+    <label name="start_period"/>
+    <field name="start_period"/>
+    <label name="end_period"/>
+    <field name="end_period"/>
+    <label name="es_vat_book_type"/>
+    <field name="es_vat_book_type"/>
+</form>
diff -r 2007d9a3de34 -r 898b35dc3d46 view/vat_book_list.xml
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/view/vat_book_list.xml    Mon Apr 12 09:26:14 2021 +0200
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
+this repository contains the full copyright notices and license terms. -->
+<tree>
+    <field name="invoice_date"/>
+    <field name="invoice" expand="1"/>
+    <field name="party_tax_identifier" expand="1"/>
+    <field name="party" expand="2"/>
+    <field name="tax" expand="1"/>
+    <field name="base_amount"/>
+    <field name="tax_amount"/>
+    <field name="surcharge_tax" expand="1"/>
+    <field name="surcharge_tax_amount"/>
+</tree>

Reply via email to