mirror of
https://gitlab.com/flectra-hq/flectra.git
synced 2026-10-26 07:50:05 -05:00
[PATCH] Upstream patch - 15102021
This commit is contained in:
1 parent
f045c58122
commit
b3109b9d19
11 files changed
+924
-4
No files matched your search
@@ -277,7 +277,7 @@ class AccountReconcileModel(models.Model):
|
||||
self.ensure_one()
|
||||
balance = base_line_dict['balance']
|
||||
tax_type = tax.type_tax_use
|
||||
is_refund = (tax_type == 'sale' and balance < 0) or (tax_type == 'purchase' and balance > 0)
|
||||
is_refund = (tax_type == 'sale' and balance > 0) or (tax_type == 'purchase' and balance < 0)
|
||||
|
||||
res = tax.compute_all(balance, is_refund=is_refund)
|
||||
|
||||
|
||||
@@ -199,7 +199,7 @@
|
||||
<p t-if="o.invoice_incoterm_id" name="incoterm">
|
||||
<strong>Incoterm: </strong><span t-field="o.invoice_incoterm_id.code"/> - <span t-field="o.invoice_incoterm_id.name"/>
|
||||
</p>
|
||||
<div id="qrcode" t-if="o.display_qr_code">
|
||||
<div id="qrcode" t-if="o.display_qr_code and o.amount_residual > 0">
|
||||
<p t-if="qr_code_urls.get(o.id)">
|
||||
<strong class="text-center">Scan me with your banking app.</strong><br/><br/>
|
||||
<img class="border border-dark rounded" t-att-src="qr_code_urls[o.id]"/>
|
||||
|
||||
@@ -82,6 +82,8 @@ class ProviderGrid(models.Model):
|
||||
total_delivery += line.price_total
|
||||
if not line.product_id or line.is_delivery:
|
||||
continue
|
||||
if line.product_id.type == "service":
|
||||
continue
|
||||
qty = line.product_uom._compute_quantity(line.product_uom_qty, line.product_id.uom_id)
|
||||
weight += (line.product_id.weight or 0.0) * qty
|
||||
volume += (line.product_id.volume or 0.0) * qty
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from . import common
|
||||
from . import test_manual
|
||||
@@ -0,0 +1,694 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from flectra import fields
|
||||
from flectra.tests.common import Form
|
||||
from flectra.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 7073',
|
||||
"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 7073',
|
||||
"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.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 Anónimo",
|
||||
"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_cerrocastor = cls.env['res.partner'].create({
|
||||
"name": "Cerro Castor (Tierra del Fuego)",
|
||||
"is_company": 1,
|
||||
"city": "Ushuaia",
|
||||
"state_id": cls.env.ref("base.state_ar_v").id,
|
||||
"country_id": cls.env.ref("base.ar").id,
|
||||
"street": "Ruta 3 km 26",
|
||||
"email": "info@cerrocastor.com",
|
||||
"phone": "(+00) (11) 4444 5556",
|
||||
"website": "http://www.cerrocastor.com",
|
||||
'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_cmr = cls.env['res.partner'].create({
|
||||
"name": "Concejo Municipal de Rosario (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": "Cordoba 501",
|
||||
"email": "info@example.com.ar",
|
||||
"phone": "(+54) (341) 222 3333",
|
||||
"website": "http://www.concejorosario.gov.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_expresso = cls.env['res.partner'].create({
|
||||
"name": "Expresso",
|
||||
"is_company": 1,
|
||||
"city": "Barcelona",
|
||||
"zip": "11002",
|
||||
"country_id": cls.env.ref("base.es").id,
|
||||
"street": "La gran avenida 123",
|
||||
"email": "info@expresso.com",
|
||||
"phone": "(+00) (11) 222 3333",
|
||||
"website": "http://www.expresso.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": "Belgrano Cargas Y Logistica S (Mipyme)",
|
||||
"is_company": 1,
|
||||
"city": "Buenos Aires",
|
||||
"zip": "1425",
|
||||
"state_id": cls.env.ref("base.state_ar_c").id,
|
||||
"country_id": cls.env.ref("base.ar").id,
|
||||
"street": "Av. Santa Fe 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')
|
||||
|
||||
# ==== 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,
|
||||
'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,
|
||||
'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,
|
||||
'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,
|
||||
'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,
|
||||
'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,
|
||||
'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)",
|
||||
# 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 Flectra (VAT Exempt)',
|
||||
'uom_id': uom_unit.id,
|
||||
'uom_po_id': uom_unit.id,
|
||||
"list_price": 80.0,
|
||||
'type': 'consu',
|
||||
'default_code': 'EXENTO',
|
||||
'taxes_id': [(6, 0, cls.tax_iva_exento.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 = {}
|
||||
|
||||
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")
|
||||
|
||||
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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": "out_invoice",
|
||||
"company_id": self.company_ri,
|
||||
"invoice_line_ids": [
|
||||
{'product_id': self.service_iva_21}
|
||||
],
|
||||
},
|
||||
'test_invoice_2': {
|
||||
"ref": "test_invoice_2: Invoice to CMR with vat 21, 27 and 10,5",
|
||||
"partner_id": self.res_partner_cmr,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": "out_invoice",
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_exento, 'price_unit': 100.0, 'quantity': 1},
|
||||
],
|
||||
},
|
||||
'test_invoice_5': {
|
||||
'ref': 'test_invoice_5: Invoice to ADHOC with all type of taxes',
|
||||
"partner_id": self.res_partner_adhoc,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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 cerro castor, fiscal position changes taxes to exempt',
|
||||
"partner_id": self.res_partner_cerrocastor,
|
||||
"journal_id": self.sale_expo_journal_ri,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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 expresso, fiscal position changes tax to exempt (type 4 because it have services)',
|
||||
"partner_id": self.res_partner_expresso,
|
||||
"journal_id": self.sale_expo_journal_ri,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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 expresso, fiscal position changes tax to exempt (type 1 because only products)',
|
||||
"partner_id": self.res_partner_expresso,
|
||||
"journal_id": self.sale_expo_journal_ri,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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 expresso, fiscal position changes tax to exempt (type 2 because only service)',
|
||||
"partner_id": self.res_partner_expresso,
|
||||
"journal_id": self.sale_expo_journal_ri,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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 expresso, fiscal position changes tax to exempt (type 1 because it have products only, used to test refund of expo)',
|
||||
"partner_id": self.res_partner_expresso,
|
||||
"journal_id": self.sale_expo_journal_ri,
|
||||
"invoice_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id": invoice_user_id,
|
||||
"invoice_payment_term_id": payment_term_id,
|
||||
"move_type": 'out_invoice',
|
||||
"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_user_id = values['invoice_user_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')
|
||||
line_form.name = 'xxxx'
|
||||
line_form.account_id = self.company_data['default_account_revenue']
|
||||
invoice = invoice_form.save()
|
||||
self.demo_invoices[key] = invoice
|
||||
|
||||
# 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': afip_ws,
|
||||
'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:
|
||||
if line.get('display_type'):
|
||||
invoice_line_form.display_type = line.get('display_type')
|
||||
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')})
|
||||
|
||||
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):
|
||||
res = self.env['account.tax'].with_context(active_test=False).search([
|
||||
('type_tax_use', '=', 'sale'),
|
||||
('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})
|
||||
_logger.log(25, 'Using %s rate %s' % (currency.name, currency.rate))
|
||||
@@ -0,0 +1,99 @@
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
from . import common
|
||||
from flectra.tests import tagged
|
||||
|
||||
|
||||
@tagged('-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 Flectra')
|
||||
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')
|
||||
|
||||
# Cerro Castor > IVA Liberado – Ley Nº 19.640 > Compras / Ventas Zona Franca > IVA Exento
|
||||
invoice = self._create_invoice({'partner': self.res_partner_cerrocastor})
|
||||
self.assertEqual(invoice.fiscal_position_id, self._search_fp('Compras / Ventas Zona Franca'))
|
||||
|
||||
# Expresso > Cliente / Proveedor del Exterior > > IVA Exento
|
||||
invoice = self._create_invoice({'partner': self.res_partner_expresso})
|
||||
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'])
|
||||
@@ -135,6 +135,8 @@ class MrpProduction(models.Model):
|
||||
return False
|
||||
if not all(line.lot_id for line in mo.move_raw_ids.filtered(lambda sm: sm.has_tracking != 'none').move_line_ids):
|
||||
return False
|
||||
if mo.product_id.tracking != 'none' and not mo.lot_producing_id:
|
||||
return False
|
||||
return True
|
||||
|
||||
return self.filtered(filter_in)
|
||||
@@ -735,3 +735,75 @@ class TestSubcontractingTracking(TransactionCase):
|
||||
self.assertEqual(avail_qty_comp1, -todo_nb)
|
||||
self.assertEqual(avail_qty_comp2, -todo_nb)
|
||||
self.assertEqual(avail_qty_finished, todo_nb)
|
||||
|
||||
def test_flow_tracked_backorder02(self):
|
||||
""" Both component and finished product are tracked by lot. """
|
||||
todo_nb = 4
|
||||
resupply_sub_on_order_route = self.env['stock.location.route'].search([('name', '=', 'Resupply Subcontractor on Order')])
|
||||
finished_product, component = self.env['product.product'].create([{
|
||||
'name': 'SuperProduct',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
}, {
|
||||
'name': 'Component',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
'route_ids': [(4, resupply_sub_on_order_route.id)],
|
||||
}])
|
||||
|
||||
bom_form = Form(self.env['mrp.bom'])
|
||||
bom_form.type = 'subcontract'
|
||||
bom_form.subcontractor_ids.add(self.subcontractor_partner1)
|
||||
bom_form.product_tmpl_id = finished_product.product_tmpl_id
|
||||
with bom_form.bom_line_ids.new() as bom_line:
|
||||
bom_line.product_id = component
|
||||
bom_line.product_qty = 1
|
||||
bom = bom_form.save()
|
||||
|
||||
finished_lot, component_lot = self.env['stock.production.lot'].create([{
|
||||
'name': 'lot_%s' % product.name,
|
||||
'product_id': product.id,
|
||||
'company_id': self.env.company.id,
|
||||
} for product in [finished_product, component]])
|
||||
|
||||
self.env['stock.quant']._update_available_quantity(component, self.env.ref('stock.stock_location_stock'), todo_nb, lot_id=component_lot)
|
||||
|
||||
# Create a receipt picking from the subcontractor
|
||||
picking_form = Form(self.env['stock.picking'])
|
||||
picking_form.picking_type_id = self.env.ref('stock.picking_type_in')
|
||||
picking_form.partner_id = self.subcontractor_partner1
|
||||
with picking_form.move_ids_without_package.new() as move:
|
||||
move.product_id = finished_product
|
||||
move.product_uom_qty = todo_nb
|
||||
picking_receipt = picking_form.save()
|
||||
picking_receipt.action_confirm()
|
||||
|
||||
mo = self.env['mrp.production'].search([('bom_id', '=', bom.id)])
|
||||
|
||||
# Process the delivery of the components
|
||||
compo_picking = mo.picking_ids
|
||||
compo_picking.action_assign()
|
||||
wizard_data = compo_picking.button_validate()
|
||||
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
for qty in [3, 1]:
|
||||
# Record the receiption of <qty> finished products
|
||||
picking_receipt = self.env['stock.picking'].search([('partner_id', '=', self.subcontractor_partner1.id), ('state', '!=', 'done')])
|
||||
action = picking_receipt.action_record_components()
|
||||
mo = self.env['mrp.production'].browse(action['res_id'])
|
||||
mo_form = Form(mo.with_context(**action['context']), view=action['view_id'])
|
||||
mo_form.qty_producing = qty
|
||||
mo_form.lot_producing_id = finished_lot
|
||||
with mo_form.move_line_raw_ids.edit(0) as ml:
|
||||
ml.lot_id = component_lot
|
||||
mo = mo_form.save()
|
||||
mo.subcontracting_record_component()
|
||||
|
||||
# Validate the picking and create a backorder
|
||||
wizard_data = picking_receipt.button_validate()
|
||||
if qty == 3:
|
||||
wizard = Form(self.env[wizard_data['res_model']].with_context(wizard_data['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
self.assertEqual(picking_receipt.state, 'done')
|
||||
@@ -1818,5 +1818,6 @@ class StockMove(models.Model):
|
||||
for move in self:
|
||||
domains.append([('product_id', '=', move.product_id.id), ('location_id', '=', move.location_dest_id.id)])
|
||||
static_domain = [('state', 'in', ['confirmed', 'partially_available']), ('procure_method', '=', 'make_to_stock')]
|
||||
moves_to_reserve = self.env['stock.move'].search(expression.AND([static_domain, expression.OR(domains)]))
|
||||
moves_to_reserve = self.env['stock.move'].search(expression.AND([static_domain, expression.OR(domains)]),
|
||||
order='priority desc, date asc, id asc')
|
||||
moves_to_reserve._action_assign()
|
||||
@@ -521,7 +521,7 @@ class ProcurementGroup(models.Model):
|
||||
# Search all confirmed stock_moves and try to assign them
|
||||
domain = self._get_moves_to_assign_domain(company_id)
|
||||
moves_to_assign = self.env['stock.move'].search(domain, limit=None,
|
||||
order='priority desc, date asc')
|
||||
order='priority desc, date asc, id asc')
|
||||
for moves_chunk in split_every(100, moves_to_assign.ids):
|
||||
self.env['stock.move'].browse(moves_chunk).sudo()._action_assign()
|
||||
if use_new_cursor:
|
||||
|
||||
@@ -2092,3 +2092,50 @@ class TestStockFlow(TestStockCommon):
|
||||
# Validates the two receipts => It should raise an error as there is duplicate SN.
|
||||
with self.assertRaises(ValidationError):
|
||||
(receipt_1 | receipt_2).button_validate()
|
||||
|
||||
def test_assign_qty_to_first_move(self):
|
||||
""" Suppose two out picking waiting for an available quantity. When receiving such
|
||||
a quantity, the latter should be assign to the picking with the highest priority
|
||||
and the earliest scheduled date. """
|
||||
def create_picking(type, from_loc, to_loc, sequence=10, delay=0):
|
||||
picking = self.PickingObj.create({
|
||||
'picking_type_id': type,
|
||||
'location_id': from_loc,
|
||||
'location_dest_id': to_loc,
|
||||
})
|
||||
self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'sequence': sequence,
|
||||
'date': fields.Datetime.add(fields.Datetime.now(), second=delay),
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 1,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking.id,
|
||||
'location_id': from_loc,
|
||||
'location_dest_id': to_loc,
|
||||
})
|
||||
picking.action_confirm()
|
||||
return picking
|
||||
|
||||
def validate_picking(picking):
|
||||
res_dict = picking.button_validate()
|
||||
wizard = Form(self.env[(res_dict.get('res_model'))].with_context(res_dict['context'])).save()
|
||||
wizard.process()
|
||||
|
||||
out01 = create_picking(self.picking_type_out, self.stock_location, self.customer_location)
|
||||
out02 = create_picking(self.picking_type_out, self.stock_location, self.customer_location, sequence=2, delay=1)
|
||||
in01 = create_picking(self.picking_type_in, self.supplier_location, self.stock_location, delay=2)
|
||||
|
||||
validate_picking(in01)
|
||||
self.assertEqual(out01.state, 'assigned')
|
||||
self.assertEqual(out02.state, 'confirmed')
|
||||
|
||||
validate_picking(out01)
|
||||
|
||||
out03 = create_picking(self.picking_type_out, self.stock_location, self.customer_location, delay=3)
|
||||
out03.priority = "1"
|
||||
in02 = create_picking(self.picking_type_in, self.supplier_location, self.stock_location, delay=4)
|
||||
|
||||
validate_picking(in02)
|
||||
self.assertEqual(out02.state, 'confirmed')
|
||||
self.assertEqual(out03.state, 'assigned')
|
||||
Reference in new issue
Block a user