Initial commit: L10N_Americas packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:52 +02:00
commit 12b27ce151
714 changed files with 79328 additions and 0 deletions

View file

@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
from . import test_manual

View file

@ -0,0 +1,778 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields
from odoo.tests.common import Form, tagged
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
import random
import logging
import time
_logger = logging.getLogger(__name__)
class TestAr(AccountTestInvoicingCommon):
@classmethod
def setUpClass(cls, chart_template_ref='l10n_ar.l10nar_ri_chart_template'):
super(TestAr, cls).setUpClass(chart_template_ref=chart_template_ref)
# ==== Company ====
cls.company_data['company'].write({
'parent_id': cls.env.ref('base.main_company').id,
'currency_id': cls.env.ref('base.ARS').id,
'name': '(AR) Responsable Inscripto (Unit Tests)',
"l10n_ar_afip_start_date": time.strftime('%Y-01-01'),
'l10n_ar_gross_income_type': 'local',
'l10n_ar_gross_income_number': '901-21885123',
})
cls.company_ri = cls.company_data['company']
cls.company_ri.partner_id.write({
'name': '(AR) Responsable Inscripto (Unit Tests)',
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_IVARI").id,
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': '30111111118',
"street": 'Calle Falsa 123',
"city": 'Rosario',
"country_id": cls.env.ref("base.ar").id,
"state_id": cls.env.ref("base.state_ar_s").id,
"zip": '2000',
"phone": '+1 555 123 8069',
"email": 'info@example.com',
"website": 'www.example.com',
})
cls.partner_ri = cls.company_ri.partner_id
# ==== Company MONO ====
cls.company_mono = cls.setup_company_data('(AR) Monotributista (Unit Tests)', chart_template=cls.env.ref('l10n_ar.l10nar_base_chart_template'))['company']
cls.company_mono.write({
'parent_id': cls.env.ref('base.main_company').id,
'currency_id': cls.env.ref('base.ARS').id,
'name': '(AR) Monotributista (Unit Tests)',
"l10n_ar_afip_start_date": time.strftime('%Y-01-01'),
'l10n_ar_gross_income_type': 'exempt',
})
cls.company_mono.partner_id.write({
'name': '(AR) Monotributista (Unit Tests)',
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_RM").id,
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': '20222222223',
"street": 'Calle Falsa 123',
"city": 'Rosario',
"country_id": cls.env.ref("base.ar").id,
"state_id": cls.env.ref("base.state_ar_s").id,
"zip": '2000',
"phone": '+1 555 123 8069',
"email": 'info@example.com',
"website": 'www.example.com',
})
cls.partner_mono = cls.company_mono.partner_id
# ==== Bank Account ====
cls.bank_account_ri = cls.env['res.partner.bank'].create({
'acc_number': '7982898111100056688080',
'partner_id': cls.company_ri.partner_id.id,
'company_id': cls.company_ri.id,
})
# ==== Partners / Customers ====
cls.partner_afip = cls.env.ref("l10n_ar.partner_afip")
cls.res_partner_adhoc = cls.env['res.partner'].create({
"name": "ADHOC SA",
"is_company": 1,
"city": "Rosario",
"zip": "2000",
"state_id": cls.env.ref("base.state_ar_s").id,
"country_id": cls.env.ref("base.ar").id,
"street": "Ovidio Lagos 41 bis",
"email": "info@adhoc.com.ar",
"phone": "(+54) (341) 208 0203",
"website": "http://www.adhoc.com.ar",
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': "30714295698",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_IVARI").id,
})
cls.partner_cf = cls.env['res.partner'].create({
"name": "Consumidor Final Anonimo",
"l10n_latam_identification_type_id": cls.env.ref('l10n_ar.it_Sigd').id,
"l10n_ar_afip_responsibility_type_id": cls.env.ref("l10n_ar.res_CF").id,
})
cls.res_partner_gritti_mono = cls.env['res.partner'].create({
"name": "Gritti Agrimensura (Monotributo)",
"is_company": 1,
"city": "Rosario",
"zip": "2000",
"state_id": cls.env.ref("base.state_ar_s").id,
"country_id": cls.env.ref("base.ar").id,
"street": "Calle Falsa 123",
"email": "info@example.com.ar",
"phone": "(+54) (341) 111 2222",
"website": "http://www.grittiagrimensura.com",
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': "27320732811",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_RM").id,
})
cls.res_partner_montana_sur = cls.env['res.partner'].create({
"name": "Montana Sur",
"is_company": 1,
"city": "San Martin de los Andes",
"state_id": cls.env.ref("base.state_ar_v").id,
"country_id": cls.env.ref("base.ar").id,
"street": "Ruta 3 km 26",
"email": "contacto@montanasur.com.ar.com",
"phone": "(+54) (297) 4876 123",
"website": "http://www.montanasur.com.ar/",
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': "27333333339",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_IVA_LIB").id,
})
cls.res_partner_servicios_globales = cls.env['res.partner'].create({
"name": "Servicios Globales SRL (IVA Sujeto Exento)",
"is_company": 1,
"city": "Rosario",
"zip": "2000",
"state_id": cls.env.ref("base.state_ar_s").id,
"country_id": cls.env.ref("base.ar").id,
"street": "Bv. Oroño 789",
"email": "contacto@serviciosglobales.com.ar",
"phone": "(+54) (341) 333 4444",
"website": "http://www.serviciosglobales.com.ar/",
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': "30684679372",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_IVAE").id,
})
cls.res_partner_barcelona_food = cls.env['res.partner'].create({
"name": "Barcelona Food",
"is_company": 1,
"city": "Barcelona",
"zip": "11002",
"country_id": cls.env.ref("base.es").id,
"street": "La gran avenida 123",
"email": "info@barcelonafoods.com",
"phone": "(+00) (11) 222 3333",
"website": "http://www.barcelonafoods.com/",
'l10n_latam_identification_type_id': cls.env.ref("l10n_latam_base.it_fid").id,
'vat': "2222333344445555",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_EXT").id,
})
cls.partner_mipyme = cls.env['res.partner'].create({
"name": "Trenes Argentinos (Mipyme)",
"is_company": 1,
"city": "Buenos Aires",
"zip": "1524",
"state_id": cls.env.ref("base.state_ar_c").id,
"country_id": cls.env.ref("base.ar").id,
"street": "Santa Cruz 4636",
"email": "mipyme@example.com",
"phone": "(123)-456-7890",
"website": "http://www.mypime-inc.com",
'l10n_latam_identification_type_id': cls.env.ref("l10n_ar.it_cuit").id,
'vat': "30714101443",
'l10n_ar_afip_responsibility_type_id': cls.env.ref("l10n_ar.res_IVARI").id,
})
cls.partner_mipyme_ex = cls.partner_mipyme.copy({'name': 'MiPyme Exento', 'l10n_ar_afip_responsibility_type_id': cls.env.ref('l10n_ar.res_IVAE').id})
# ==== Taxes ====
cls.tax_21 = cls._search_tax(cls, 'iva_21')
cls.tax_27 = cls._search_tax(cls, 'iva_27')
cls.tax_0 = cls._search_tax(cls, 'iva_0')
cls.tax_10_5 = cls._search_tax(cls, 'iva_105')
cls.tax_no_gravado = cls._search_tax(cls, 'iva_no_gravado')
cls.tax_perc_iibb = cls._search_tax(cls, 'percepcion_iibb_ba')
cls.tax_iva_exento = cls._search_tax(cls, 'iva_exento')
cls.tax_national = cls.env['account.tax'].create({
"name": "National Tax",
"description": "National Tax",
"amount": "4",
"amount_type": "percent",
"type_tax_use": "sale",
"country_id": cls.env.ref("base.ar").id,
"company_id": cls.company_ri.id,
"tax_group_id": cls.env.ref("l10n_ar.tax_group_national_taxes").id,
})
cls.tax_internal = cls.env['account.tax'].create({
"name": "Internal Tax",
"description": "Internal Tax",
"amount": "3",
"amount_type": "percent",
"type_tax_use": "sale",
"country_id": cls.env.ref("base.ar").id,
"company_id": cls.company_ri.id,
"tax_group_id": cls.env.ref("l10n_ar.tax_impuestos_internos").id,
})
cls.tax_other = cls.env['account.tax'].create({
"name": "Other Tax",
"description": "Other Tax",
"amount": "100",
"amount_type": "fixed",
"type_tax_use": "sale",
"country_id": cls.env.ref("base.ar").id,
"company_id": cls.company_ri.id,
"tax_group_id": cls.env.ref("l10n_ar.tax_group_otros_impuestos").id,
})
cls.tax_21_purchase = cls._search_tax(cls, 'iva_21', type_tax_use='purchase')
cls.tax_no_gravado_purchase = cls._search_tax(cls, 'iva_no_gravado', type_tax_use='purchase')
# ==== Products ====
uom_unit = cls.env.ref('uom.product_uom_unit')
uom_hour = cls.env.ref('uom.product_uom_hour')
cls.product_iva_21 = cls.env['product.product'].create({
'name': 'Large Cabinet (VAT 21)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'lst_price': 320.0,
'standard_price': 800.0,
'type': "consu",
'default_code': 'E-COM07',
})
cls.service_iva_27 = cls.env['product.product'].create({
# demo 'product_product_telefonia'
'name': 'Telephone service (VAT 27)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'lst_price': 130.0,
'standard_price': 250.0,
'type': 'service',
'default_code': 'TELEFONIA',
'taxes_id': [(6, 0, cls.tax_27.ids)],
})
cls.product_iva_cero = cls.env['product.product'].create({
# demo 'product_product_cero'
'name': 'Non-industrialized animals and vegetables (VAT Zero)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'list_price': 160.0,
'standard_price': 200.0,
'type': 'consu',
'default_code': 'CERO',
'taxes_id': [(6, 0, cls.tax_0.ids)],
})
cls.product_iva_105 = cls.env['product.product'].create({
# demo 'product.product_product_27'
'name': 'Laptop Customized (VAT 10,5)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'standard_price': 4500.0,
'type': 'consu',
'default_code': '10,5',
'taxes_id': [(6, 0, cls.tax_10_5.ids)],
})
cls.service_iva_21 = cls.env['product.product'].create({
# demo data product.product_product_2
'name': 'Virtual Home Staging (VAT 21)',
'uom_id': uom_hour.id,
'uom_po_id': uom_hour.id,
'list_price': 38.25,
'standard_price': 45.5,
'type': 'service',
'default_code': 'VAT 21',
'taxes_id': [(6, 0, cls.tax_21.ids)],
})
cls.product_no_gravado = cls.env['product.product'].create({
# demo data product_product_no_gravado
'name': 'Untaxed concepts (VAT NT)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'list_price': 40.00,
'standard_price': 50.0,
'type': 'consu',
'default_code': 'NOGRAVADO',
'taxes_id': [(6, 0, cls.tax_no_gravado.ids)],
})
cls.product_iva_105_perc = cls.product_iva_105.copy({
# product.product_product_25
"name": "Laptop E5023 (VAT 10,5)",
"standard_price": 3280.0,
# agregamos percecipn aplicada y sufrida tambien
'taxes_id': [(6, 0, [cls.tax_10_5.id, cls.tax_perc_iibb.id])],
})
cls.product_iva_exento = cls.env['product.product'].create({
# demo product_product_exento
'name': 'Book: Development in Odoo (VAT Exempt)',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'standard_price': 100.0,
"list_price": 80.0,
'type': 'consu',
'default_code': 'EXENTO',
'taxes_id': [(6, 0, cls.tax_iva_exento.ids)],
})
cls.service_wo_tax = cls.env['product.product'].create({
# demo product_product_quote_despacho
'name': 'Service WO TAX',
'type': 'service',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'default_code': 'AFIP_DESPACHO',
})
cls.service_iva_no_gravado = cls.env['product.product'].create({
# demo product_product_arancel
'name': 'Server VAT Untaxed',
'type': 'service',
'uom_id': uom_unit.id,
'uom_po_id': uom_unit.id,
'default_code': 'AFIP_ARANCEL',
"supplier_taxes_id": [(6, 0, (cls.tax_no_gravado_purchase).ids)],
})
# ==== Document Types ====
cls.document_type = {
'invoice_a': cls.env.ref('l10n_ar.dc_a_f'),
'credit_note_a': cls.env.ref('l10n_ar.dc_a_nc'),
'invoice_b': cls.env.ref('l10n_ar.dc_b_f'),
'credit_note_b': cls.env.ref('l10n_ar.dc_b_nc'),
'invoice_e': cls.env.ref('l10n_ar.dc_e_f'),
'invoice_mipyme_a': cls.env.ref('l10n_ar.dc_fce_a_f'),
'invoice_mipyme_b': cls.env.ref('l10n_ar.dc_fce_b_f'),
}
# ==== Journals ====
cls.sale_expo_journal_ri = cls.env["account.journal"].create({
'name': "Expo Sales Journal",
'company_id': cls.company_ri.id,
'type': "sale",
'code': "S0002",
'l10n_latam_use_documents': "True",
'l10n_ar_afip_pos_number': 2,
'l10n_ar_afip_pos_partner_id': cls.partner_ri.id,
'l10n_ar_afip_pos_system': "FEERCEL",
'refund_sequence': False,
})
# ==== Invoices ====
cls.demo_invoices = {}
cls.demo_credit_notes = {}
cls.demo_bills = {}
def _create_test_invoices_like_demo(self, use_current_date=True):
""" Create in the unit tests the same invoices created in demo data """
payment_term_id = self.env.ref("account.account_payment_term_end_following_month")
invoice_user_id = self.env.user
incoterm = self.env.ref("account.incoterm_EXW")
decimal_price = self.env.ref('product.decimal_price')
decimal_price.digits = 4
invoices_to_create = {
'test_invoice_1': {
"ref": "test_invoice_1: Invoice to gritti support service, vat 21",
"partner_id": self.res_partner_gritti_mono,
"invoice_payment_term_id": payment_term_id,
"move_type": "out_invoice",
"invoice_date": "2021-03-01",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21}
],
},
'test_invoice_2': {
"ref": "test_invoice_2: Invoice to Servicios Globales with vat 21, 27 and 10,5",
"partner_id": self.res_partner_servicios_globales,
"invoice_payment_term_id": payment_term_id,
"move_type": "out_invoice",
"invoice_date": "2021-03-05",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 2}
],
},
'test_invoice_3': {
"ref": "test_invoice_3: Invoice to ADHOC with vat cero and 21",
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-01",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
{'product_id': self.product_iva_cero, 'price_unit': 200.0, 'quantity': 1}
],
},
'test_invoice_4': {
'ref': 'test_invoice_4: Invoice to ADHOC with vat exempt and 21',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-01",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.1234, 'quantity': 5},
{'product_id': self.product_iva_exento, 'price_unit': 100.5678, 'quantity': 1},
],
},
'test_invoice_5': {
'ref': 'test_invoice_5: Invoice to ADHOC with all type of taxes',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 2},
{'product_id': self.product_no_gravado, 'price_unit': 50.0, 'quantity': 10},
{'product_id': self.product_iva_cero, 'price_unit': 200.0, 'quantity': 1},
{'product_id': self.product_iva_exento, 'price_unit': 100.0, 'quantity': 1}
],
},
'test_invoice_6': {
'ref': 'test_invoice_6: Invoice to Montana Sur, fiscal position changes taxes to exempt',
"partner_id": self.res_partner_montana_sur,
"journal_id": self.sale_expo_journal_ri,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-03",
"company_id": self.company_ri,
"invoice_incoterm_id": incoterm,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 2},
{'product_id': self.product_no_gravado, 'price_unit': 50.0, 'quantity': 10},
{'product_id': self.product_iva_cero, 'price_unit': 200.0, 'quantity': 1},
{'product_id': self.product_iva_exento, 'price_unit': 100.0, 'quantity': 1},
],
},
'test_invoice_7': {
'ref': 'test_invoice_7: Export invoice to Barcelona food, fiscal position changes tax to exempt (type 4 because it have services)',
"partner_id": self.res_partner_barcelona_food,
"journal_id": self.sale_expo_journal_ri,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-03",
"company_id": self.company_ri,
"invoice_incoterm_id": incoterm,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 2},
{'product_id': self.product_no_gravado, 'price_unit': 50.0, 'quantity': 10},
{'product_id': self.product_iva_cero, 'price_unit': 200.0, 'quantity': 1},
{'product_id': self.product_iva_exento, 'price_unit': 100.0, 'quantity': 1},
],
},
'test_invoice_8': {
'ref': 'test_invoice_8: Invoice to consumidor final',
"partner_id": self.partner_cf,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 642.0, 'quantity': 1},
],
},
'test_invoice_10': {
'ref': 'test_invoice_10; Invoice to ADHOC in USD and vat 21',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 1000.0, 'quantity': 5},
],
"currency_id": self.env.ref("base.USD"),
},
'test_invoice_11': {
'ref': 'test_invoice_11: Invoice to ADHOC with many lines in order to prove rounding error, with 4 decimals of precision for the currency and 2 decimals for the product the error apperar',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 1.12, 'quantity': 1, 'name': 'Support Services 1'},
{'product_id': self.service_iva_21, 'price_unit': 1.12, 'quantity': 1, 'name': 'Support Services 2'},
{'product_id': self.service_iva_21, 'price_unit': 1.12, 'quantity': 1, 'name': 'Support Services 3'},
{'product_id': self.service_iva_21, 'price_unit': 1.12, 'quantity': 1, 'name': 'Support Services 4'},
],
},
'test_invoice_12': {
'ref': 'test_invoice_12: Invoice to ADHOC with many lines in order to test rounding error, it is required to use a 4 decimal precision in prodct in order to the error occur',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 15.7076, 'quantity': 1, 'name': 'Support Services 1'},
{'product_id': self.service_iva_21, 'price_unit': 5.3076, 'quantity': 2, 'name': 'Support Services 2'},
{'product_id': self.service_iva_21, 'price_unit': 3.5384, 'quantity': 2, 'name': 'Support Services 3'},
{'product_id': self.service_iva_21, 'price_unit': 1.6376, 'quantity': 2, 'name': 'Support Services 4'},
],
},
'test_invoice_13': {
'ref': 'test_invoice_13: Invoice to ADHOC with many lines in order to test zero amount invoices y rounding error. it is required to set the product decimal precision to 4 and change 260.59 for 260.60 in order to reproduce the error',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 24.3, 'quantity': 3, 'name': 'Support Services 1'},
{'product_id': self.service_iva_21, 'price_unit': 260.59, 'quantity': 1, 'name': 'Support Services 2'},
{'product_id': self.service_iva_21, 'price_unit': 48.72, 'quantity': 1, 'name': 'Support Services 3'},
{'product_id': self.service_iva_21, 'price_unit': 13.666, 'quantity': 1, 'name': 'Support Services 4'},
{'product_id': self.service_iva_21, 'price_unit': 11.329, 'quantity': 2, 'name': 'Support Services 5'},
{'product_id': self.service_iva_21, 'price_unit': 68.9408, 'quantity': 1, 'name': 'Support Services 6'},
{'product_id': self.service_iva_21, 'price_unit': 4.7881, 'quantity': 2, 'name': 'Support Services 7'},
{'product_id': self.service_iva_21, 'price_unit': 12.0625, 'quantity': 2, 'name': 'Support Services 8'},
],
},
'test_invoice_14': {
'ref': 'test_invoice_14: Export invoice to Barcelona food, fiscal position changes tax to exempt (type 1 because only products)',
"partner_id": self.res_partner_barcelona_food,
"journal_id": self.sale_expo_journal_ri,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-20",
"company_id": self.company_ri,
"invoice_incoterm_id": incoterm,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
],
},
'test_invoice_15': {
'ref': 'test_invoice_15: Export invoice to Barcelona food, fiscal position changes tax to exempt (type 2 because only service)',
"partner_id": self.res_partner_barcelona_food,
"journal_id": self.sale_expo_journal_ri,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-20",
"company_id": self.company_ri,
"invoice_incoterm_id": incoterm,
"invoice_line_ids": [
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
],
},
'test_invoice_16': {
'ref': 'test_invoice_16: Export invoice to Barcelona food, fiscal position changes tax to exempt (type 1 because it have products only, used to test refund of expo)',
"partner_id": self.res_partner_barcelona_food,
"journal_id": self.sale_expo_journal_ri,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-22",
"company_id": self.company_ri,
"invoice_incoterm_id": incoterm,
"invoice_line_ids": [
{'product_id': self.product_iva_105, 'price_unit': 642.0, 'quantity': 5},
],
},
'test_invoice_17': {
'ref': 'test_invoice_17: Invoice to ADHOC with 100%% of discount',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 24.3, 'quantity': 3, 'name': 'Support Services 8', 'discount': 100},
],
},
'test_invoice_18': {
'ref': 'test_invoice_18: Invoice to ADHOC with 100%% of discount and with different VAT aliquots',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 24.3, 'quantity': 3, 'name': 'Support Services 8', 'discount': 100},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1, 'discount': 100},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 1},
],
},
'test_invoice_19': {
'ref': 'test_invoice_19: Invoice to ADHOC with multiple taxes and perceptions',
"partner_id": self.res_partner_adhoc,
"invoice_payment_term_id": payment_term_id,
"move_type": 'out_invoice',
"invoice_date": "2021-03-13",
"company_id": self.company_ri,
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 24.3, 'quantity': 3, 'name': 'Support Services 8'},
{'product_id': self.service_iva_27, 'price_unit': 250.0, 'quantity': 1},
{'product_id': self.product_iva_105_perc, 'price_unit': 3245.0, 'quantity': 1},
],
}
}
for key, values in invoices_to_create.items():
with Form(self.env['account.move'].with_context(default_move_type=values['move_type'])) as invoice_form:
invoice_form.ref = values['ref']
invoice_form.partner_id = values['partner_id']
invoice_form.invoice_payment_term_id = values['invoice_payment_term_id']
if not use_current_date:
invoice_form.invoice_date = values['invoice_date']
if values.get('invoice_incoterm_id'):
invoice_form.invoice_incoterm_id = values['invoice_incoterm_id']
for line in values['invoice_line_ids']:
with invoice_form.invoice_line_ids.new() as line_form:
line_form.product_id = line.get('product_id')
line_form.price_unit = line.get('price_unit')
line_form.quantity = line.get('quantity')
if line.get('tax_ids'):
line_form.tax_ids = line.get('tax_ids')
line_form.name = 'xxxx'
line_form.account_id = self.company_data['default_account_revenue']
invoice = invoice_form.save()
self.demo_invoices[key] = invoice
def _create_invoice_from_dict(self, values, use_current_date=True):
if not values.get('invoice_payment_term_id'):
values['invoice_payment_term_id'] = self.env.ref("account.account_payment_term_end_following_month")
if use_current_date:
values.pop('invoice_date', False)
for key, value in values.items():
if key.endswith("_id"):
values[key] = value.id
for line in values['invoice_line_ids']:
for key, value in line.items():
if key.endswith("_id"):
line[key] = value.id
values['invoice_line_ids'] = [(0, 0, line_data) for line_data in values['invoice_line_ids']]
return self.env['account.move'].with_context(default_move_type=values['move_type']).create(values)
# Helpers
@classmethod
def _get_afip_pos_system_real_name(cls):
return {'PREPRINTED': 'II_IM'}
def _create_journal(self, afip_ws, data=None):
""" Create a journal of a given AFIP ws type.
If there is a problem because we are using a AFIP certificate that is already been in use then change the certificate and try again """
data = data or {}
afip_ws = afip_ws.upper()
pos_number = str(random.randint(0, 99999))
if 'l10n_ar_afip_pos_number' in data:
pos_number = data.get('l10n_ar_afip_pos_number')
values = {'name': '%s %s' % (afip_ws.replace('WS', ''), pos_number),
'type': 'sale',
'code': pos_number,
'l10n_ar_afip_pos_system': self._get_afip_pos_system_real_name().get(afip_ws),
'l10n_ar_afip_pos_number': pos_number,
'l10n_latam_use_documents': True,
'company_id': self.env.company.id,
'l10n_ar_afip_pos_partner_id': self.partner_ri.id,
'sequence': 1}
values.update(data)
journal = self.env['account.journal'].create(values)
_logger.info('Created journal %s for company %s', journal.name, self.env.company.name)
return journal
def _create_invoice(self, data=None, invoice_type='out_invoice'):
data = data or {}
with Form(self.env['account.move'].with_context(default_move_type=invoice_type)) as invoice_form:
invoice_form.partner_id = data.get('partner', self.partner)
if 'in_' not in invoice_type:
invoice_form.journal_id = data.get('journal', self.journal)
if data.get('document_type'):
invoice_form.l10n_latam_document_type_id = data.get('document_type')
if data.get('document_number'):
invoice_form.l10n_latam_document_number = data.get('document_number')
if data.get('incoterm'):
invoice_form.invoice_incoterm_id = data.get('incoterm')
if data.get('currency'):
invoice_form.currency_id = data.get('currency')
for line in data.get('lines', [{}]):
with invoice_form.invoice_line_ids.new() as invoice_line_form:
invoice_line_form.display_type = line.get('display_type', 'product')
if line.get('display_type') in ('line_note', 'line_section'):
invoice_line_form.name = line.get('name', 'not invoice line')
else:
invoice_line_form.product_id = line.get('product', self.product_iva_21)
invoice_line_form.quantity = line.get('quantity', 1)
invoice_line_form.price_unit = line.get('price_unit', 100)
invoice_form.invoice_date = invoice_form.date
invoice = invoice_form.save()
return invoice
def _create_invoice_product(self, data=None):
data = data or {}
return self._create_invoice(data)
def _create_invoice_service(self, data=None):
data = data or {}
newlines = []
for line in data.get('lines', [{}]):
line.update({'product': self.service_iva_27})
newlines.append(line)
data.update({'lines': newlines})
return self._create_invoice(data)
def _create_invoice_product_service(self, data=None):
data = data or {}
newlines = []
for line in data.get('lines', [{}]):
line.update({'product': self.product_iva_21})
newlines.append(line)
data.update({'lines': newlines + [{'product': self.service_iva_27}]})
return self._create_invoice(data)
def _create_credit_note(self, invoice, data=None):
data = data or {}
refund_wizard = self.env['account.move.reversal'].with_context({'active_ids': [invoice.id], 'active_model': 'account.move'}).create({
'reason': data.get('reason', 'Mercadería defectuosa'),
'refund_method': data.get('refund_method', 'refund'),
'journal_id': invoice.journal_id.id})
forced_document_type = data.get('document_type')
if forced_document_type:
refund_wizard.l10n_latam_document_type_id = forced_document_type.id
res = refund_wizard.reverse_moves()
refund = self.env['account.move'].browse(res['res_id'])
return refund
def _create_debit_note(self, invoice, data=None):
data = data or {}
debit_note_wizard = self.env['account.debit.note'].with_context(
{'active_ids': [invoice.id], 'active_model': 'account.move', 'default_copy_lines': True}).create({
'reason': data.get('reason', 'Mercadería defectuosa')})
res = debit_note_wizard.create_debit()
debit_note = self.env['account.move'].browse(res['res_id'])
return debit_note
def _search_tax(self, tax_type, type_tax_use='sale'):
res = self.env['account.tax'].with_context(active_test=False).search([
('type_tax_use', '=', type_tax_use),
('company_id', '=', self.env.company.id),
('tax_group_id', '=', self.env.ref('l10n_ar.tax_group_' + tax_type).id)], limit=1)
self.assertTrue(res, '%s Tax was not found' % (tax_type))
return res
def _search_fp(self, name):
return self.env['account.fiscal.position'].search([('company_id', '=', self.env.company.id), ('name', '=', name)])
def _post(self, invoice):
invoice.action_post()
self.assertEqual(invoice.state, 'posted')
def _prepare_multicurrency_values(self):
# Enable multi currency
self.env.user.write({'groups_id': [(4, self.env.ref('base.group_multi_currency').id)]})
# Set ARS as main currency
self._set_today_rate(self.env.ref('base.ARS'), 1.0)
# Set Rates for USD currency
self._set_today_rate(self.env.ref('base.USD'), 1.0 / 162.013)
def _set_today_rate(self, currency, value):
rate_obj = self.env['res.currency.rate']
rate = rate_obj.search([('currency_id', '=', currency.id), ('name', '=', fields.Date.to_string(fields.Date.today())),
('company_id', '=', self.env.company.id)])
if rate:
rate.rate = value
else:
rate_obj.create({'company_id': self.env.company.id, 'currency_id': currency.id, 'rate': value})

View file

@ -0,0 +1,173 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import common
from odoo.tests import tagged
from odoo.tools.float_utils import float_split_str
@tagged('post_install_l10n', '-at_install', 'post_install')
class TestManual(common.TestAr):
@classmethod
def setUpClass(cls):
super(TestManual, cls).setUpClass()
cls.journal = cls._create_journal(cls, 'preprinted')
cls.partner = cls.res_partner_adhoc
cls._create_test_invoices_like_demo(cls)
def test_01_create_invoice(self):
""" Create and validate an invoice for a Responsable Inscripto
* Proper set the current user company
* Properly set the tax amount of the product / partner
* Proper fiscal position (this case not fiscal position is selected)
"""
invoice = self._create_invoice()
self.assertEqual(invoice.company_id, self.company_ri, 'created with wrong company')
self.assertEqual(invoice.amount_tax, 21, 'invoice taxes are not properly set')
self.assertEqual(invoice.amount_total, 121.0, 'invoice taxes has not been applied to the total')
self.assertEqual(invoice.l10n_latam_document_type_id, self.document_type['invoice_a'], 'selected document type should be Factura A')
self._post(invoice)
self.assertEqual(invoice.state, 'posted', 'invoice has not been validate in Odoo')
self.assertEqual(invoice.name, 'FA-A %05d-00000002' % self.journal.l10n_ar_afip_pos_number, 'Invoice number is wrong')
def test_02_fiscal_position(self):
# ADHOC SA > IVA Responsable Inscripto > Without Fiscal Positon
invoice = self._create_invoice({'partner': self.partner})
self.assertFalse(invoice.fiscal_position_id, 'Fiscal position should be set to empty')
# Consumidor Final > IVA Responsable Inscripto > Without Fiscal Positon
invoice = self._create_invoice({'partner': self.partner_cf})
self.assertFalse(invoice.fiscal_position_id, 'Fiscal position should be set to empty')
# Montana Sur > IVA Liberado - Ley Nº 19.640 > Compras / Ventas Zona Franca > IVA Exento
invoice = self._create_invoice({'partner': self.res_partner_montana_sur})
self.assertEqual(invoice.fiscal_position_id, self._search_fp('Compras / Ventas Zona Franca'))
# Barcelona food > Cliente / Proveedor del Exterior > > IVA Exento
invoice = self._create_invoice({'partner': self.res_partner_barcelona_food})
self.assertEqual(invoice.fiscal_position_id, self._search_fp('Compras / Ventas al exterior'))
def test_03_corner_cases(self):
""" Mono partner of type Service and VAT 21 """
self._post(self.demo_invoices['test_invoice_1'])
def test_04_corner_cases(self):
""" Exento partner with multiple VAT types 21, 27 and 10,5' """
self._post(self.demo_invoices['test_invoice_2'])
def test_05_corner_cases(self):
""" RI partner with VAT 0 and 21 """
self._post(self.demo_invoices['test_invoice_3'])
def test_06_corner_cases(self):
""" RI partner with VAT exempt and 21 """
self._post(self.demo_invoices['test_invoice_4'])
def test_07_corner_cases(self):
""" RI partner with all type of taxes """
self._post(self.demo_invoices['test_invoice_5'])
def test_08_corner_cases(self):
""" Consumidor Final """
self._post(self.demo_invoices['test_invoice_8'])
def test_09_corner_cases(self):
""" RI partner with many lines in order to prove rounding error, with 4 decimals of precision for the
currency and 2 decimals for the product the error appear """
self._post(self.demo_invoices['test_invoice_11'])
def test_10_corner_cases(self):
""" RI partner with many lines in order to test rounding error, it is required to use a 4 decimal precision
in product in order to the error occur """
self._post(self.demo_invoices['test_invoice_12'])
def test_11_corner_cases(self):
""" RI partner with many lines in order to test zero amount invoices y rounding error. it is required to
set the product decimal precision to 4 and change 260,59 for 260.60 in order to reproduce the error """
self._post(self.demo_invoices['test_invoice_13'])
def test_12_corner_cases(self):
""" RI partner with 100%% of discount """
self._post(self.demo_invoices['test_invoice_17'])
def test_13_corner_cases(self):
""" RI partner with 100%% of discount and with different VAT aliquots """
self._post(self.demo_invoices['test_invoice_18'])
def test_14_corner_cases(self):
""" Responsable Inscripto" in USD and VAT 21 """
self._prepare_multicurrency_values()
self._post(self.demo_invoices['test_invoice_10'])
def test_15_corner_cases(self):
""" RI partner with VAT exempt and 21. Test price unit digits """
self._post(self.demo_invoices['test_invoice_4'])
decimal_price_digits_setting = self.env.ref('product.decimal_price').digits
invoice_line_ids = self.demo_invoices['test_invoice_4'].invoice_line_ids
for line in invoice_line_ids:
l10n_ar_line_prices = line._l10n_ar_prices_and_taxes()
_unitary_part, l10n_ar_price_unit_decimal_part = float_split_str(l10n_ar_line_prices['price_unit'], decimal_price_digits_setting)
len_l10n_ar_price_unit_digits = len(l10n_ar_price_unit_decimal_part)
_unitary_part, line_price_unit_decimal_part = float_split_str(line.price_unit, decimal_price_digits_setting)
len_line_price_unit_digits = len(line_price_unit_decimal_part)
if len_l10n_ar_price_unit_digits == len_line_price_unit_digits == decimal_price_digits_setting:
self.assertEqual(l10n_ar_price_unit_decimal_part, line_price_unit_decimal_part)
def _get_simple_detail_ar_tax(self, invoice):
""" Get the simple detail_ar_tax list of tuples with the vat group name and the amount
[("vat_taxes", float), ("other_taxes", float)] """
return [
(item.get('group'), item.get('tax_amount'))
for item in invoice._l10n_ar_get_invoice_totals_for_report().get('detail_ar_tax')
]
def test_16_invoice_b_tax_breakdown_1(self):
""" Display Both VAT and Other Taxes """
invoice1 = self._create_invoice_from_dict({
'ref': 'test_invoice_20: Final Consumer Invoice B with multiple vat/perceptions/internal/other/national taxes',
"move_type": 'out_invoice',
"partner_id": self.partner_cf,
"company_id": self.company_ri,
"invoice_date": "2021-03-20",
"invoice_line_ids": [
{'product_id': self.service_iva_21, 'price_unit': 124.3, 'quantity': 3, 'name': 'Support Services 8',
'tax_ids': [(6, 0, [self.tax_21.id, self.tax_perc_iibb.id])]},
{'product_id': self.service_iva_27, 'price_unit': 2250.0, 'quantity': 1,
'tax_ids': [(6, 0, [self.tax_27.id, self.tax_national.id])]},
{'product_id': self.product_iva_105_perc, 'price_unit': 1740.0, 'quantity': 1,
'tax_ids': [(6, 0, [self.tax_10_5.id, self.tax_internal.id])]},
{'product_id': self.product_iva_105_perc, 'price_unit': 10000.0, 'quantity': 1,
'tax_ids': [(6, 0, [self.tax_0.id, self.tax_other.id])]},
],
})
self.assertEqual(self._get_simple_detail_ar_tax(invoice1), [("vat", 868.51), ("other", 142.20)])
def test_17_invoice_b_tax_breakdown_2(self):
""" Display only Other Taxes (VAT taxes are 0) """
invoice2 = self._create_invoice_from_dict({
'ref': 'test_invoice_21: inal Consumer Invoice B with 0 tax and internal tax',
"move_type": 'out_invoice',
"partner_id": self.partner_cf,
"company_id": self.company_ri,
"invoice_date": "2021-03-20",
"invoice_line_ids": [
{'product_id': self.product_iva_105_perc, 'price_unit': 10000.0, 'quantity': 1,
'tax_ids': [(6, 0, [self.tax_no_gravado.id, self.tax_internal.id])]},
],
})
self.assertEqual(self._get_simple_detail_ar_tax(invoice2), [("vat", 0.0), ("other", 300.0)])
def test_18_invoice_b_tax_breakdown_3(self):
""" Display only Other Taxes (VAT taxes are 0 and non other taxes) """
invoice3 = self._create_invoice_from_dict({
'ref': 'test_invoice_22: Final Consumer Invoice B with 0 only',
"move_type": 'out_invoice',
"partner_id": self.partner_cf,
"company_id": self.company_ri,
"invoice_date": "2021-03-20",
"invoice_line_ids": [
{'product_id': self.product_iva_105_perc, 'price_unit': 10000.0, 'quantity': 1,
'tax_ids': [(6, 0, [self.tax_no_gravado.id])]},
],
})
self.assertEqual(self._get_simple_detail_ar_tax(invoice3), [("vat", 0.0)])