[IMP] l10n_es_edi_facturae: import facturae
Allows for importing FACe XML files. task-3387230 X-original-commit: 6b970d73b8feac1903e271770f7df77fc2d96b71 Part-of: odoo/odoo#137321
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
from collections import defaultdict
|
||||
import re
|
||||
|
||||
from odoo import fields, models, api, _
|
||||
from collections import defaultdict
|
||||
from markupsafe import escape, Markup
|
||||
|
||||
from odoo import fields, models, api, _, Command
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tools import float_repr, date_utils
|
||||
from odoo.tools.xml_utils import cleanup_xml_node
|
||||
from odoo.tools.xml_utils import cleanup_xml_node, find_xml_value
|
||||
|
||||
PHONE_CLEAN_TABLE = str.maketrans({" ": None, "-": None, "(": None, ")": None, "+": None})
|
||||
COUNTRY_CODE_MAP = {
|
||||
@@ -379,3 +382,233 @@ class AccountMove(models.Model):
|
||||
def _get_edi_doc_attachments_to_export(self):
|
||||
# EXTENDS 'account'
|
||||
return super()._get_edi_doc_attachments_to_export() + self.l10n_es_edi_facturae_xml_id
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# IMPORT
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def _get_edi_decoder(self, file_data, new=False):
|
||||
def is_facturae(tree):
|
||||
return tree.tag == '{http://www.facturae.es/Facturae/2014/v3.2.1/Facturae}Facturae'
|
||||
|
||||
if file_data['type'] == 'xml' and is_facturae(file_data['xml_tree']):
|
||||
return self._import_invoice_facturae
|
||||
|
||||
return super()._get_edi_decoder(file_data, new=new)
|
||||
|
||||
def _import_invoice_facturae(self, invoice, file_data, new=False):
|
||||
tree = file_data['xml_tree']
|
||||
is_bill = invoice.move_type.startswith('in_')
|
||||
partner = self._import_get_partner(tree, is_bill)
|
||||
self._import_invoice_facturae_invoices(invoice, partner, tree)
|
||||
|
||||
def _import_get_partner(self, tree, is_bill):
|
||||
# If we're dealing with a vendor bill, then the partner is the seller party, if an invoice then it's the buyer.
|
||||
party = tree.xpath('//SellerParty') if is_bill else tree.xpath('//BuyerParty')
|
||||
if party:
|
||||
partner_vals = self._import_extract_partner_values(party[0])
|
||||
return self._import_create_or_retrieve_partner(partner_vals)
|
||||
return None
|
||||
|
||||
def _import_extract_partner_values(self, party_node):
|
||||
name = find_xml_value('.//CorporateName|.//Name', party_node)
|
||||
first_surname = find_xml_value('.//FirstSurname', party_node)
|
||||
second_surname = find_xml_value('.//SecondSurname', party_node)
|
||||
phone = find_xml_value('.//Telephone', party_node)
|
||||
mail = find_xml_value('.//ElectronicMail', party_node)
|
||||
country_code = find_xml_value('.//CountryCode', party_node)
|
||||
vat = find_xml_value('.//TaxIdentificationNumber', party_node)
|
||||
|
||||
full_name = ' '.join(part for part in [name, first_surname, second_surname] if part)
|
||||
|
||||
return {'name': full_name, 'vat': vat, 'phone': phone, 'email': mail, 'country_code': country_code}
|
||||
|
||||
def _import_create_or_retrieve_partner(self, partner_vals):
|
||||
name = partner_vals['name']
|
||||
vat = partner_vals['vat']
|
||||
phone = partner_vals['phone']
|
||||
mail = partner_vals['email']
|
||||
country_code = partner_vals['country_code']
|
||||
|
||||
partner = self.env['res.partner']._retrieve_partner(name=name, vat=vat, phone=phone, mail=mail)
|
||||
|
||||
if not partner and name:
|
||||
partner_vals = {'name': name, 'email': mail, 'phone': phone}
|
||||
country = self.env['res.country'].search([('code', '=', country_code.lower())]) if country_code else False
|
||||
if country:
|
||||
partner_vals['country_id'] = country.id
|
||||
partner = self.env['res.partner'].create(partner_vals)
|
||||
if vat and self.env['res.partner']._run_vat_test(vat, country):
|
||||
partner.vat = vat
|
||||
|
||||
return partner
|
||||
|
||||
def _import_invoice_facturae_invoices(self, invoice, partner, tree):
|
||||
invoices = tree.xpath('//Invoice')
|
||||
if not invoices:
|
||||
return
|
||||
|
||||
self._import_invoice_facturae_invoice(invoice, partner, invoices[0])
|
||||
|
||||
# There might be other invoices inside the facturae.
|
||||
for node in invoices[1:]:
|
||||
other_invoice = invoice.create({
|
||||
'journal_id': invoice.journal_id.id,
|
||||
'move_type': invoice.move_type
|
||||
})
|
||||
with other_invoice._get_edi_creation():
|
||||
self._import_invoice_facturae_invoice(other_invoice, partner, node)
|
||||
other_invoice.message_post(body=escape(_("Created from attachment in %s")) % invoice._get_html_link())
|
||||
|
||||
def _import_invoice_facturae_invoice(self, invoice, partner, tree):
|
||||
logs = []
|
||||
|
||||
# ==== move_type ====
|
||||
invoice_total = find_xml_value('.//InvoiceTotal', tree)
|
||||
is_refund = float(invoice_total) < 0 if invoice_total else False
|
||||
if is_refund:
|
||||
invoice.move_type = "in_refund" if invoice.move_type.startswith("in_") else "out_refund"
|
||||
ref_multiplier = -1.0 if is_refund else 1.0
|
||||
|
||||
# ==== partner_id ====
|
||||
if partner:
|
||||
invoice.partner_id = partner
|
||||
else:
|
||||
logs.append(_("Customer/Vendor could not be found and could not be created due to missing data in the XML."))
|
||||
|
||||
# ==== currency_id ====
|
||||
invoice_currency_code = find_xml_value('.//InvoiceCurrencyCode', tree)
|
||||
if invoice_currency_code:
|
||||
currency = self.env['res.currency'].search([('name', '=', invoice_currency_code)], limit=1)
|
||||
if currency:
|
||||
invoice.currency_id = currency
|
||||
else:
|
||||
logs.append(_("Could not retrieve currency: %s. Did you enable the multicurrency option "
|
||||
"and activate the currency?", invoice_currency_code))
|
||||
|
||||
# ==== invoice date ====
|
||||
if issue_date := find_xml_value('.//IssueDate', tree):
|
||||
invoice.invoice_date = issue_date
|
||||
|
||||
# ==== invoice_date_due ====
|
||||
if end_date := find_xml_value('.//InstallmentDueDate', tree):
|
||||
invoice.invoice_date_due = end_date
|
||||
|
||||
# ==== ref ====
|
||||
if invoice_number := find_xml_value('.//InvoiceNumber', tree):
|
||||
invoice.ref = invoice_number
|
||||
|
||||
# ==== narration ====
|
||||
invoice.narration = "\n".join(
|
||||
ref.text
|
||||
for ref in tree.xpath('.//LegalReference')
|
||||
if ref.text
|
||||
)
|
||||
|
||||
# === invoice_line_ids ===
|
||||
logs += self._import_invoice_fill_lines(invoice, tree, ref_multiplier)
|
||||
|
||||
body = Markup("<strong>%s</strong>") % _("Invoice imported from Factura-E XML file.")
|
||||
|
||||
if logs:
|
||||
body += Markup("<ul>%s</ul>") \
|
||||
% Markup().join(Markup("<li>%s</li>") % log for log in logs)
|
||||
|
||||
invoice.message_post(body=body)
|
||||
|
||||
return logs
|
||||
|
||||
def _import_invoice_fill_lines(self, invoice, tree, ref_multiplier):
|
||||
lines = tree.xpath('.//InvoiceLine')
|
||||
logs = []
|
||||
vals_list = []
|
||||
for line in lines:
|
||||
line_vals = {'move_id': invoice.id}
|
||||
|
||||
# ==== name ====
|
||||
if item_description := find_xml_value('.//ItemDescription', line):
|
||||
product = self._search_product_for_import(item_description)
|
||||
if product:
|
||||
line_vals['product_id'] = product.id
|
||||
else:
|
||||
logs.append(_("The product '%s' could not be found.", item_description))
|
||||
line_vals['name'] = item_description
|
||||
|
||||
# ==== quantity ====
|
||||
line_vals['quantity'] = find_xml_value('.//Quantity', line) or 1
|
||||
|
||||
# ==== price_unit ====
|
||||
price_unit = find_xml_value('.//UnitPriceWithoutTax', line)
|
||||
line_vals['price_unit'] = ref_multiplier * float(price_unit) if price_unit else 1.0
|
||||
|
||||
# ==== discount ====
|
||||
discounts = line.xpath('.//DiscountRate')
|
||||
discount_rate = 0.0
|
||||
for discount in discounts:
|
||||
discount_rate += float(discount.text)
|
||||
|
||||
charges = line.xpath('.//ChargeRate')
|
||||
charge_rate = 0.0
|
||||
for charge in charges:
|
||||
charge_rate += float(charge.text)
|
||||
|
||||
discount_rate -= charge_rate
|
||||
line_vals['discount'] = discount_rate
|
||||
|
||||
# ==== tax_ids ====
|
||||
taxes_withheld_nodes = line.xpath('.//TaxesWithheld/Tax')
|
||||
taxes_outputs_nodes = line.xpath('.//TaxesOutputs/Tax')
|
||||
is_purchase = invoice.move_type.startswith('in')
|
||||
tax_ids = []
|
||||
logs += self._import_fill_invoice_line_taxes(invoice, line_vals, tax_ids, taxes_outputs_nodes, False, is_purchase)
|
||||
logs += self._import_fill_invoice_line_taxes(invoice, line_vals, tax_ids, taxes_withheld_nodes, True, is_purchase)
|
||||
line_vals['tax_ids'] = [Command.set(tax_ids)]
|
||||
vals_list.append(line_vals)
|
||||
|
||||
invoice.invoice_line_ids = self.env['account.move.line'].create(vals_list)
|
||||
return logs
|
||||
|
||||
def _import_fill_invoice_line_taxes(self, invoice, line_vals, tax_ids, tax_nodes, is_withheld, is_purchase):
|
||||
logs = []
|
||||
for tax_node in tax_nodes:
|
||||
tax_rate = find_xml_value('.//TaxRate', tax_node)
|
||||
if tax_rate:
|
||||
# Since the 'TaxRate' node isn't guaranteed to be a percentage, we can find out by
|
||||
# applying the tax rate on the taxable base, and if it's equal to the tax amount
|
||||
# then we can say this is a percentage, otherwise a fixed amount.
|
||||
taxable_base = find_xml_value('.//TaxableBase/TotalAmount', tax_node)
|
||||
tax_amount = find_xml_value('.//TaxAmount/TotalAmount', tax_node)
|
||||
is_fixed = False
|
||||
|
||||
if taxable_base and tax_amount and invoice.currency_id.compare_amounts(float(taxable_base) * (float(tax_rate) / 100), float(tax_amount)) != 0:
|
||||
is_fixed = True
|
||||
|
||||
tax_excl = self._search_tax_for_import(invoice.company_id, float(tax_rate), is_fixed, is_withheld, is_purchase, price_included=False)
|
||||
|
||||
if tax_excl:
|
||||
tax_ids.append(tax_excl.id)
|
||||
elif tax_incl := self._search_tax_for_import(invoice.company_id, float(tax_rate), is_fixed, is_withheld, is_purchase, price_included=True):
|
||||
tax_ids.append(tax_incl)
|
||||
line_vals['price_unit'] *= (1.0 + float(tax_rate) / 100.0)
|
||||
else:
|
||||
logs.append(_("Could not retrieve the tax: %s %% for line '%s'.", tax_rate, line_vals.get('name', "")))
|
||||
|
||||
return logs
|
||||
|
||||
def _search_tax_for_import(self, company, amount, is_fixed, is_withheld, is_purchase, price_included):
|
||||
taxes = self.env['account.tax'].search([
|
||||
('company_id', '=', company.id),
|
||||
('amount', '=', -1.0 * amount if is_withheld else amount),
|
||||
('amount_type', '=', 'fixed' if is_fixed else 'percent'),
|
||||
('type_tax_use', '=', 'purchase' if is_purchase else 'sale'),
|
||||
('price_include', '=', price_included),
|
||||
], limit=1)
|
||||
|
||||
return taxes
|
||||
|
||||
def _search_product_for_import(self, item_description):
|
||||
# Exported Odoo XML will have item_description = "[default_code] name".
|
||||
# We can check if it follows the same format and search for the product with the default code and the name.
|
||||
code_and_name = re.match(r"(\[(?P<default_code>.*?)\]\s)?(?P<name>.*)", item_description).groupdict()
|
||||
product = self.env['product.product']._retrieve_product(**code_and_name)
|
||||
return product
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<fac:Facturae xmlns:fac="http://www.facturae.es/Facturae/2014/v3.2.1/Facturae">
|
||||
<FileHeader>
|
||||
<SchemaVersion>3.2.1</SchemaVersion>
|
||||
<Modality>I</Modality>
|
||||
<InvoiceIssuerType>EM</InvoiceIssuerType>
|
||||
<Batch>
|
||||
<BatchIdentifier>INV/2023/00005</BatchIdentifier>
|
||||
<InvoicesCount>1</InvoicesCount>
|
||||
<TotalInvoicesAmount>
|
||||
<TotalAmount>2186.20</TotalAmount>
|
||||
</TotalInvoicesAmount>
|
||||
<TotalOutstandingAmount>
|
||||
<TotalAmount>2186.20</TotalAmount>
|
||||
</TotalOutstandingAmount>
|
||||
<TotalExecutableAmount>
|
||||
<TotalAmount>2186.20</TotalAmount>
|
||||
</TotalExecutableAmount>
|
||||
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
|
||||
</Batch>
|
||||
</FileHeader>
|
||||
<Parties>
|
||||
<SellerParty>
|
||||
<TaxIdentification>
|
||||
<PersonTypeCode>F</PersonTypeCode>
|
||||
<ResidenceTypeCode>R</ResidenceTypeCode>
|
||||
<TaxIdentificationNumber>ES59962470K</TaxIdentificationNumber>
|
||||
</TaxIdentification>
|
||||
<Individual>
|
||||
<AddressInSpain>
|
||||
<Address>A</Address>
|
||||
<PostCode>22591</PostCode>
|
||||
<Town>Candasnos</Town>
|
||||
<Province>Zaragoza</Province>
|
||||
<CountryCode>ESP</CountryCode>
|
||||
</AddressInSpain>
|
||||
<ContactDetails>
|
||||
<WebAddress>http://www.esexample.com</WebAddress>
|
||||
<ElectronicMail>info@company.esexample.com</ElectronicMail>
|
||||
</ContactDetails>
|
||||
</Individual>
|
||||
</SellerParty>
|
||||
<BuyerParty>
|
||||
<TaxIdentification>
|
||||
<PersonTypeCode>J</PersonTypeCode>
|
||||
<ResidenceTypeCode>E</ResidenceTypeCode>
|
||||
<TaxIdentificationNumber>US12345677</TaxIdentificationNumber>
|
||||
</TaxIdentification>
|
||||
<LegalEntity>
|
||||
<CorporateName>Azure Interior</CorporateName>
|
||||
<TradeName>Azure Interior</TradeName>
|
||||
<OverseasAddress>
|
||||
<Address>4557 De Silva St</Address>
|
||||
<PostCodeAndTown>94538 Fremont</PostCodeAndTown>
|
||||
<Province>California</Province>
|
||||
<CountryCode>USA</CountryCode>
|
||||
</OverseasAddress>
|
||||
<ContactDetails>
|
||||
<Telephone>8709310505</Telephone>
|
||||
<WebAddress>http://www.azure-interior.com</WebAddress>
|
||||
<ElectronicMail>azure.Interior24@example.com</ElectronicMail>
|
||||
</ContactDetails>
|
||||
</LegalEntity>
|
||||
</BuyerParty>
|
||||
</Parties>
|
||||
<Invoices>
|
||||
<Invoice>
|
||||
<InvoiceHeader>
|
||||
<InvoiceNumber>INV/2023/00005</InvoiceNumber>
|
||||
<InvoiceDocumentType>FC</InvoiceDocumentType>
|
||||
<InvoiceClass>OO</InvoiceClass>
|
||||
</InvoiceHeader>
|
||||
<InvoiceIssueData>
|
||||
<IssueDate>2023-08-01</IssueDate>
|
||||
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
|
||||
<TaxCurrencyCode>EUR</TaxCurrencyCode>
|
||||
<LanguageName>en</LanguageName>
|
||||
</InvoiceIssueData>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>67.20</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>0.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>1799.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>0.00</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
<InvoiceTotals>
|
||||
<TotalGrossAmount>2119.00</TotalGrossAmount>
|
||||
<TotalGrossAmountBeforeTaxes>2119.00</TotalGrossAmountBeforeTaxes>
|
||||
<TotalTaxOutputs>67.20</TotalTaxOutputs>
|
||||
<TotalTaxesWithheld>0.00</TotalTaxesWithheld>
|
||||
<InvoiceTotal>2186.20</InvoiceTotal>
|
||||
<TotalOutstandingAmount>2186.20</TotalOutstandingAmount>
|
||||
<TotalExecutableAmount>2186.20</TotalExecutableAmount>
|
||||
</InvoiceTotals>
|
||||
<Items>
|
||||
<InvoiceLine>
|
||||
<ItemDescription>[E-COM07] Large Cabinet</ItemDescription>
|
||||
<Quantity>1.0</Quantity>
|
||||
<UnitOfMeasure>01</UnitOfMeasure>
|
||||
<UnitPriceWithoutTax>320.00</UnitPriceWithoutTax>
|
||||
<TotalCost>320.00</TotalCost>
|
||||
<GrossAmount>320.00</GrossAmount>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>67.20</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
</InvoiceLine>
|
||||
<InvoiceLine>
|
||||
<ItemDescription>[E-COM09] Large Desk</ItemDescription>
|
||||
<Quantity>1.0</Quantity>
|
||||
<UnitOfMeasure>01</UnitOfMeasure>
|
||||
<UnitPriceWithoutTax>1799.00</UnitPriceWithoutTax>
|
||||
<TotalCost>1799.00</TotalCost>
|
||||
<GrossAmount>1799.00</GrossAmount>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>0.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>1799.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>0.00</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
</InvoiceLine>
|
||||
</Items>
|
||||
<LegalLiterals>
|
||||
<LegalReference>Terms and conditions.</LegalReference>
|
||||
</LegalLiterals>
|
||||
<PaymentDetails>
|
||||
<Installment>
|
||||
<InstallmentDueDate>2023-08-31</InstallmentDueDate>
|
||||
</Installment>
|
||||
</PaymentDetails>
|
||||
</Invoice>
|
||||
<Invoice>
|
||||
<InvoiceHeader>
|
||||
<InvoiceNumber>INV/2023/00006</InvoiceNumber>
|
||||
<InvoiceDocumentType>FC</InvoiceDocumentType>
|
||||
<InvoiceClass>OO</InvoiceClass>
|
||||
</InvoiceHeader>
|
||||
<InvoiceIssueData>
|
||||
<IssueDate>2023-07-01</IssueDate>
|
||||
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
|
||||
<TaxCurrencyCode>EUR</TaxCurrencyCode>
|
||||
<LanguageName>en</LanguageName>
|
||||
</InvoiceIssueData>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>960.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>201.60</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
<InvoiceTotals>
|
||||
<TotalGrossAmount>960.00</TotalGrossAmount>
|
||||
<TotalGrossAmountBeforeTaxes>960.00</TotalGrossAmountBeforeTaxes>
|
||||
<TotalTaxOutputs>201.60</TotalTaxOutputs>
|
||||
<TotalTaxesWithheld>0.00</TotalTaxesWithheld>
|
||||
<InvoiceTotal>1161.60</InvoiceTotal>
|
||||
<TotalOutstandingAmount>1161.60</TotalOutstandingAmount>
|
||||
<TotalExecutableAmount>1161.60</TotalExecutableAmount>
|
||||
</InvoiceTotals>
|
||||
<Items>
|
||||
<InvoiceLine>
|
||||
<ItemDescription>[E-COM07] Large Cabinet</ItemDescription>
|
||||
<Quantity>3.0</Quantity>
|
||||
<UnitOfMeasure>01</UnitOfMeasure>
|
||||
<UnitPriceWithoutTax>320.00</UnitPriceWithoutTax>
|
||||
<TotalCost>960.00</TotalCost>
|
||||
<GrossAmount>960.00</GrossAmount>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>960.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>201.60</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
</InvoiceLine>
|
||||
</Items>
|
||||
<LegalLiterals>
|
||||
<LegalReference>Legal References.</LegalReference>
|
||||
</LegalLiterals>
|
||||
<PaymentDetails>
|
||||
<Installment>
|
||||
<InstallmentDueDate>2023-07-31</InstallmentDueDate>
|
||||
</Installment>
|
||||
</PaymentDetails>
|
||||
</Invoice>
|
||||
</Invoices>
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<ds:SignedInfo>
|
||||
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
||||
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
||||
<ds:Reference Type="http://www.w3.org/2000/09/xmldsig#Object" URI="" Id="Reference-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<ds:Transforms>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
||||
</ds:Transforms>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>cElLMko1b3H9dDrcO0nLhBA0DM7NWpAVuFDk7r+HMxM=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignatureProperties-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>1fyEQGjgpXKz9ie50e2WwFRk5kXklJNr10YHVMWBHeo=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
<ds:Reference URI="#KeyInfo-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>rUjANojsduwjEzBt8hX6sFsG9F1mwQ1X/rJhxY1MOGI=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
</ds:SignedInfo>
|
||||
<ds:SignatureValue>ecCZUOChRyxRpMRo8PaqBDKsuiWf8IOavpwLDSaD1+ZjEKF20D7GUfMoDbMTmotpw+qybmuQQZVQ
|
||||
CRpl+7efRdcIGNjKwI1hxdbwFd41SCU3HXZKJ5B6mOdJjW4uBnXce157lI4U/liyHmdRyW1K3WZc
|
||||
RCh4GrJ5M7N662JfhVOLhZaKSXFI7+s517HD5mJ4tw84UG7j3OgAfVqH3BZe+oM8boTJAUtFyNlO
|
||||
o8YWWWgvV6LNkqbKkP5J2dcrwQmaUuISeQ7+/BU0T7OfdbpFGtvz1PrI2d9YTFHrH+aWU5gZ96kX
|
||||
cmr2Ab2uGYRNXqIpHEA9DcY0pkapd6CNgPv5sg==
|
||||
</ds:SignatureValue>
|
||||
<ds:KeyInfo Id="KeyInfo-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>MIIEMTCCAxmgAwIBAgIUXMxVyBDULPbIaeKVK9bJwftsQdswDQYJKoZIhvcNAQELBQAwgacxCzAJ
|
||||
BgNVBAYTAkJFMREwDwYDVQQIDAhXYWxsb25pYTEgMB4GA1UEBwwXR3JhbmQtUm9zaWVyZS1Ib3R0
|
||||
b21vbnQxFTATBgNVBAoMDFRlc3QgT2RvbyBTQTEXMBUGA1UECwwOUiZEIEFjY291bnRpbmcxFzAV
|
||||
BgNVBAMMDlImRCBBY2NvdW50aW5nMRowGAYJKoZIhvcNAQkBFgtyZEBvZG9vLmNvbTAeFw0yMzAy
|
||||
MDkwODU1NDhaFw0zMzAyMDYwODU1NDhaMIGnMQswCQYDVQQGEwJCRTERMA8GA1UECAwIV2FsbG9u
|
||||
aWExIDAeBgNVBAcMF0dyYW5kLVJvc2llcmUtSG90dG9tb250MRUwEwYDVQQKDAxUZXN0IE9kb28g
|
||||
U0ExFzAVBgNVBAsMDlImRCBBY2NvdW50aW5nMRcwFQYDVQQDDA5SJkQgQWNjb3VudGluZzEaMBgG
|
||||
CSqGSIb3DQEJARYLcmRAb2Rvby5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx
|
||||
eId7+OTIUl/GWhSo+y+E+8yclmgdgWope+fyq4jYACSID2rrxcScAgmGcGfvcNJ7e4aUoqrGg7Ob
|
||||
FIuDbGjVg5RFUjDljzvV97em7KuPf/qHVWAVXrKkD6JgV7KRp38RLyBLdgbwZqgP0QRBM6HLl1nC
|
||||
H6TMrVtTOskjeJ9jd1VACBlMWFpaTQl4dcnM6W8o7e5OYSru6u9xuqyQQim06YyjwAuxfQEMO1OV
|
||||
n3cQlrqpbOI5SNu3vi6eb0z4n1KEw8t2zeF+QrMSNzykb3CJVOaLI8uyYcZXow7RcSy0HZNu4TZS
|
||||
nQRgODbxrFlcfSHz2hSqxWAXW4/He594Gbw9AgMBAAGjUzBRMB0GA1UdDgQWBBSjLlcJeL3Yg2aL
|
||||
L7aElSBMhQqLXjAfBgNVHSMEGDAWgBSjLlcJeL3Yg2aLL7aElSBMhQqLXjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCf9DCON7ehvD+x/EXLIDhJUzsWxyICj0T+cMw7BBrJVQ94
|
||||
6RJlR9m3sNwIy0VoYQKNJ1JGda15YVpppyJTVSj11Ig8rG6/l06ysNnzJ6+aA2ba1Z0ycFFkX+Cl
|
||||
By7sIqAn+HbAJ6M4sAzQKvoWoaFA4uSbZGLLf2NKMNGIsTBO3rtSEt8zqJBTVuX03Sxp9mJIjIXi
|
||||
HfrCCDyBtXxnnBpTnMFy74mHg2Xbu5si85nxnNzYC0RTbd7qxEO0OEBQGz5AMH/kyzNnyu9WvLlg
|
||||
lfRpb8ioiJ9ZFLNIsZtRA60R5NWDpACwADa9WnfqzH8lHnEaIXwsziU+SSAIz8kSdCwQ
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
<ds:KeyValue>
|
||||
<ds:RSAKeyValue>
|
||||
<ds:Modulus>sXiHe/jkyFJfxloUqPsvhPvMnJZoHYFqKXvn8quI2AAkiA9q68XEnAIJhnBn73DSe3uGlKKqxoOz
|
||||
mxSLg2xo1YOURVIw5Y871fe3puyrj3/6h1VgFV6ypA+iYFeykad/ES8gS3YG8GaoD9EEQTOhy5dZ
|
||||
wh+kzK1bUzrJI3ifY3dVQAgZTFhaWk0JeHXJzOlvKO3uTmEq7urvcbqskEIptOmMo8ALsX0BDDtT
|
||||
lZ93EJa6qWziOUjbt74unm9M+J9ShMPLds3hfkKzEjc8pG9wiVTmiyPLsmHGV6MO0XEstB2TbuE2
|
||||
Up0EYDg28axZXH0h89oUqsVgF1uPx3ufeBm8PQ==
|
||||
</ds:Modulus>
|
||||
<ds:Exponent>AQAB
|
||||
</ds:Exponent>
|
||||
</ds:RSAKeyValue>
|
||||
</ds:KeyValue>
|
||||
</ds:KeyInfo>
|
||||
<ds:Object>
|
||||
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="#Signature-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<xades:SignedProperties Id="SignatureProperties-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<xades:SignedSignatureProperties>
|
||||
<xades:SigningTime>2023-08-31T09:51:34.576445</xades:SigningTime>
|
||||
<xades:SigningCertificate>
|
||||
<xades:Cert>
|
||||
<xades:CertDigest>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>cP4cRSOphs7/CsqPwdGujvo7xkSkh8CfvFPAsQf/zRE=</ds:DigestValue>
|
||||
</xades:CertDigest>
|
||||
<xades:IssuerSerial>
|
||||
<ds:X509IssuerName>C=BE, CN=R&D Accounting, L=Grand-Rosiere-Hottomont, O=Test Odoo SA, OU=R&D Accounting, ST=Wallonia</ds:X509IssuerName>
|
||||
<ds:X509SerialNumber>529783975559440394003723764779526236797615751643</ds:X509SerialNumber>
|
||||
</xades:IssuerSerial>
|
||||
</xades:Cert>
|
||||
</xades:SigningCertificate>
|
||||
<xades:SignaturePolicyIdentifier>
|
||||
<xades:SignaturePolicyId>
|
||||
<xades:SigPolicyId>
|
||||
<xades:Identifier>http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf</xades:Identifier>
|
||||
<xades:Description>Política de firma electrónica para facturación electrónica con formato Facturae</xades:Description>
|
||||
</xades:SigPolicyId>
|
||||
<xades:SigPolicyHash>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>a/vDoueJq9zxWGCFwdsXdlXtkFSEek4XVtmlQCEDaE8=</ds:DigestValue>
|
||||
</xades:SigPolicyHash>
|
||||
<xades:SigPolicyQualifiers>
|
||||
<xades:SigPolicyQualifier>
|
||||
<xades:SPURI>http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf</xades:SPURI>
|
||||
</xades:SigPolicyQualifier>
|
||||
</xades:SigPolicyQualifiers>
|
||||
</xades:SignaturePolicyId>
|
||||
</xades:SignaturePolicyIdentifier>
|
||||
</xades:SignedSignatureProperties>
|
||||
<xades:SignedDataObjectProperties>
|
||||
<xades:DataObjectFormat ObjectReference="#Reference-Document-1796c070f59109773ed3d7e92a20952003e69560">
|
||||
<xades:Description/>
|
||||
<xades:ObjectIdentifier>
|
||||
<xades:Identifier Qualifier="OIDAsURN">urn:oid:1.2.840.10003.5.109.10</xades:Identifier>
|
||||
<xades:Description/>
|
||||
</xades:ObjectIdentifier>
|
||||
<xades:MimeType>text/xml</xades:MimeType>
|
||||
<xades:Encoding/>
|
||||
</xades:DataObjectFormat>
|
||||
</xades:SignedDataObjectProperties>
|
||||
</xades:SignedProperties>
|
||||
</xades:QualifyingProperties>
|
||||
</ds:Object>
|
||||
</ds:Signature>
|
||||
</fac:Facturae>
|
||||
@@ -0,0 +1,260 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
|
||||
<fac:Facturae xmlns:fac="http://www.facturae.es/Facturae/2014/v3.2.1/Facturae">
|
||||
<FileHeader>
|
||||
<SchemaVersion>3.2.1</SchemaVersion>
|
||||
<Modality>I</Modality>
|
||||
<InvoiceIssuerType>EM</InvoiceIssuerType>
|
||||
<Batch>
|
||||
<BatchIdentifier>INV/2023/00009</BatchIdentifier>
|
||||
<InvoicesCount>1</InvoicesCount>
|
||||
<TotalInvoicesAmount>
|
||||
<TotalAmount>323.20</TotalAmount>
|
||||
</TotalInvoicesAmount>
|
||||
<TotalOutstandingAmount>
|
||||
<TotalAmount>323.20</TotalAmount>
|
||||
</TotalOutstandingAmount>
|
||||
<TotalExecutableAmount>
|
||||
<TotalAmount>323.20</TotalAmount>
|
||||
</TotalExecutableAmount>
|
||||
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
|
||||
</Batch>
|
||||
</FileHeader>
|
||||
<Parties>
|
||||
<SellerParty>
|
||||
<TaxIdentification>
|
||||
<PersonTypeCode>F</PersonTypeCode>
|
||||
<ResidenceTypeCode>R</ResidenceTypeCode>
|
||||
<TaxIdentificationNumber>ES59962470K</TaxIdentificationNumber>
|
||||
</TaxIdentification>
|
||||
<Individual>
|
||||
<AddressInSpain>
|
||||
<Address>A</Address>
|
||||
<PostCode>22591</PostCode>
|
||||
<Town>Candasnos</Town>
|
||||
<Province>Zaragoza</Province>
|
||||
<CountryCode>ESP</CountryCode>
|
||||
</AddressInSpain>
|
||||
<ContactDetails>
|
||||
<WebAddress>http://www.esexample.com</WebAddress>
|
||||
<ElectronicMail>info@company.esexample.com</ElectronicMail>
|
||||
</ContactDetails>
|
||||
</Individual>
|
||||
</SellerParty>
|
||||
<BuyerParty>
|
||||
<TaxIdentification>
|
||||
<PersonTypeCode>J</PersonTypeCode>
|
||||
<ResidenceTypeCode>E</ResidenceTypeCode>
|
||||
<TaxIdentificationNumber>US12345677</TaxIdentificationNumber>
|
||||
</TaxIdentification>
|
||||
<LegalEntity>
|
||||
<CorporateName>Azure Interior</CorporateName>
|
||||
<TradeName>Azure Interior</TradeName>
|
||||
<OverseasAddress>
|
||||
<Address>4557 De Silva St</Address>
|
||||
<PostCodeAndTown>94538 Fremont</PostCodeAndTown>
|
||||
<Province>California</Province>
|
||||
<CountryCode>USA</CountryCode>
|
||||
</OverseasAddress>
|
||||
<ContactDetails>
|
||||
<Telephone>8709310505</Telephone>
|
||||
<WebAddress>http://www.azure-interior.com</WebAddress>
|
||||
<ElectronicMail>azure.Interior24@example.com</ElectronicMail>
|
||||
</ContactDetails>
|
||||
</LegalEntity>
|
||||
</BuyerParty>
|
||||
</Parties>
|
||||
<Invoices>
|
||||
<Invoice>
|
||||
<InvoiceHeader>
|
||||
<InvoiceNumber>INV/2023/00009</InvoiceNumber>
|
||||
<InvoiceDocumentType>FC</InvoiceDocumentType>
|
||||
<InvoiceClass>OO</InvoiceClass>
|
||||
</InvoiceHeader>
|
||||
<InvoiceIssueData>
|
||||
<IssueDate>2023-09-01</IssueDate>
|
||||
<InvoiceCurrencyCode>EUR</InvoiceCurrencyCode>
|
||||
<TaxCurrencyCode>EUR</TaxCurrencyCode>
|
||||
<LanguageName>en</LanguageName>
|
||||
</InvoiceIssueData>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>67.20</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
<TaxesWithheld>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>20.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>64.00</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesWithheld>
|
||||
<InvoiceTotals>
|
||||
<TotalGrossAmount>320.00</TotalGrossAmount>
|
||||
<TotalGrossAmountBeforeTaxes>320.00</TotalGrossAmountBeforeTaxes>
|
||||
<TotalTaxOutputs>67.20</TotalTaxOutputs>
|
||||
<TotalTaxesWithheld>64.00</TotalTaxesWithheld>
|
||||
<InvoiceTotal>323.20</InvoiceTotal>
|
||||
<TotalOutstandingAmount>323.20</TotalOutstandingAmount>
|
||||
<TotalExecutableAmount>323.20</TotalExecutableAmount>
|
||||
</InvoiceTotals>
|
||||
<Items>
|
||||
<InvoiceLine>
|
||||
<ItemDescription>[E-COM07] Large Cabinet</ItemDescription>
|
||||
<Quantity>1.0</Quantity>
|
||||
<UnitOfMeasure>01</UnitOfMeasure>
|
||||
<UnitPriceWithoutTax>320.00</UnitPriceWithoutTax>
|
||||
<TotalCost>320.00</TotalCost>
|
||||
<GrossAmount>320.00</GrossAmount>
|
||||
<TaxesOutputs>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>21.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>67.20</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesOutputs>
|
||||
<TaxesWithheld>
|
||||
<Tax>
|
||||
<TaxTypeCode>01</TaxTypeCode>
|
||||
<TaxRate>20.000</TaxRate>
|
||||
<TaxableBase>
|
||||
<TotalAmount>320.00</TotalAmount>
|
||||
</TaxableBase>
|
||||
<TaxAmount>
|
||||
<TotalAmount>64.00</TotalAmount>
|
||||
</TaxAmount>
|
||||
</Tax>
|
||||
</TaxesWithheld>
|
||||
</InvoiceLine>
|
||||
</Items>
|
||||
</Invoice>
|
||||
</Invoices>
|
||||
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="Signature-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<ds:SignedInfo>
|
||||
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
|
||||
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
|
||||
<ds:Reference Type="http://www.w3.org/2000/09/xmldsig#Object" URI="" Id="Reference-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<ds:Transforms>
|
||||
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
|
||||
</ds:Transforms>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>y+DiG9thnFpt6SvkhDhV/8PYAzfhj0KKTXnZjbCbfa8=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
<ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#SignatureProperties-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>jx6wLH7wWnh3HQD4ixOqAWULf2lT9BAxbbWDzxloxo4=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
<ds:Reference URI="#KeyInfo-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>pti8uQOjoaRhHT1DyGg8Z0BKxxhgQMfyrHMbdiHzS/M=</ds:DigestValue>
|
||||
</ds:Reference>
|
||||
</ds:SignedInfo>
|
||||
<ds:SignatureValue>qSZAhTS36ed1Vl0hPHZHDrY1tJtr0gcjY7sgqZgw163wdqZYiV5WWmM8l6ttc1voNrrw1XCENXMz
|
||||
Sur4lCKCQdIA400uvRM1tY8LoptM5GJswZoLnfZS2lkK8xAC8vDoBAKEaIVjuBd8Gi+Oztz6OiJG
|
||||
+EIT8mMPmZ9jCt3XExMn8dZRaTeB6XwTH8GGyvgWjXVxEbSTD+BiRthORMeNwAdl3KiCrzgrYbNu
|
||||
QaozjiZQGfpj2hJt2+zKvhpEML1nWRQUVrM2sjBxhhewQTmxavzG1xL3MHBzT5//YZYfsSNFJbE0
|
||||
imJThhtOwrZvWqgN/B8wfhrS2eZkue9w0cX2cg==
|
||||
</ds:SignatureValue>
|
||||
<ds:KeyInfo Id="KeyInfo-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<ds:X509Data>
|
||||
<ds:X509Certificate>MIIEMTCCAxmgAwIBAgIUXMxVyBDULPbIaeKVK9bJwftsQdswDQYJKoZIhvcNAQELBQAwgacxCzAJ
|
||||
BgNVBAYTAkJFMREwDwYDVQQIDAhXYWxsb25pYTEgMB4GA1UEBwwXR3JhbmQtUm9zaWVyZS1Ib3R0
|
||||
b21vbnQxFTATBgNVBAoMDFRlc3QgT2RvbyBTQTEXMBUGA1UECwwOUiZEIEFjY291bnRpbmcxFzAV
|
||||
BgNVBAMMDlImRCBBY2NvdW50aW5nMRowGAYJKoZIhvcNAQkBFgtyZEBvZG9vLmNvbTAeFw0yMzAy
|
||||
MDkwODU1NDhaFw0zMzAyMDYwODU1NDhaMIGnMQswCQYDVQQGEwJCRTERMA8GA1UECAwIV2FsbG9u
|
||||
aWExIDAeBgNVBAcMF0dyYW5kLVJvc2llcmUtSG90dG9tb250MRUwEwYDVQQKDAxUZXN0IE9kb28g
|
||||
U0ExFzAVBgNVBAsMDlImRCBBY2NvdW50aW5nMRcwFQYDVQQDDA5SJkQgQWNjb3VudGluZzEaMBgG
|
||||
CSqGSIb3DQEJARYLcmRAb2Rvby5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx
|
||||
eId7+OTIUl/GWhSo+y+E+8yclmgdgWope+fyq4jYACSID2rrxcScAgmGcGfvcNJ7e4aUoqrGg7Ob
|
||||
FIuDbGjVg5RFUjDljzvV97em7KuPf/qHVWAVXrKkD6JgV7KRp38RLyBLdgbwZqgP0QRBM6HLl1nC
|
||||
H6TMrVtTOskjeJ9jd1VACBlMWFpaTQl4dcnM6W8o7e5OYSru6u9xuqyQQim06YyjwAuxfQEMO1OV
|
||||
n3cQlrqpbOI5SNu3vi6eb0z4n1KEw8t2zeF+QrMSNzykb3CJVOaLI8uyYcZXow7RcSy0HZNu4TZS
|
||||
nQRgODbxrFlcfSHz2hSqxWAXW4/He594Gbw9AgMBAAGjUzBRMB0GA1UdDgQWBBSjLlcJeL3Yg2aL
|
||||
L7aElSBMhQqLXjAfBgNVHSMEGDAWgBSjLlcJeL3Yg2aLL7aElSBMhQqLXjAPBgNVHRMBAf8EBTAD
|
||||
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCf9DCON7ehvD+x/EXLIDhJUzsWxyICj0T+cMw7BBrJVQ94
|
||||
6RJlR9m3sNwIy0VoYQKNJ1JGda15YVpppyJTVSj11Ig8rG6/l06ysNnzJ6+aA2ba1Z0ycFFkX+Cl
|
||||
By7sIqAn+HbAJ6M4sAzQKvoWoaFA4uSbZGLLf2NKMNGIsTBO3rtSEt8zqJBTVuX03Sxp9mJIjIXi
|
||||
HfrCCDyBtXxnnBpTnMFy74mHg2Xbu5si85nxnNzYC0RTbd7qxEO0OEBQGz5AMH/kyzNnyu9WvLlg
|
||||
lfRpb8ioiJ9ZFLNIsZtRA60R5NWDpACwADa9WnfqzH8lHnEaIXwsziU+SSAIz8kSdCwQ
|
||||
</ds:X509Certificate>
|
||||
</ds:X509Data>
|
||||
<ds:KeyValue>
|
||||
<ds:RSAKeyValue>
|
||||
<ds:Modulus>sXiHe/jkyFJfxloUqPsvhPvMnJZoHYFqKXvn8quI2AAkiA9q68XEnAIJhnBn73DSe3uGlKKqxoOz
|
||||
mxSLg2xo1YOURVIw5Y871fe3puyrj3/6h1VgFV6ypA+iYFeykad/ES8gS3YG8GaoD9EEQTOhy5dZ
|
||||
wh+kzK1bUzrJI3ifY3dVQAgZTFhaWk0JeHXJzOlvKO3uTmEq7urvcbqskEIptOmMo8ALsX0BDDtT
|
||||
lZ93EJa6qWziOUjbt74unm9M+J9ShMPLds3hfkKzEjc8pG9wiVTmiyPLsmHGV6MO0XEstB2TbuE2
|
||||
Up0EYDg28axZXH0h89oUqsVgF1uPx3ufeBm8PQ==
|
||||
</ds:Modulus>
|
||||
<ds:Exponent>AQAB
|
||||
</ds:Exponent>
|
||||
</ds:RSAKeyValue>
|
||||
</ds:KeyValue>
|
||||
</ds:KeyInfo>
|
||||
<ds:Object>
|
||||
<xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" Target="#Signature-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<xades:SignedProperties Id="SignatureProperties-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<xades:SignedSignatureProperties>
|
||||
<xades:SigningTime>2023-09-01T12:16:38.095681</xades:SigningTime>
|
||||
<xades:SigningCertificate>
|
||||
<xades:Cert>
|
||||
<xades:CertDigest>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>cP4cRSOphs7/CsqPwdGujvo7xkSkh8CfvFPAsQf/zRE=</ds:DigestValue>
|
||||
</xades:CertDigest>
|
||||
<xades:IssuerSerial>
|
||||
<ds:X509IssuerName>C=BE, CN=R&D Accounting, L=Grand-Rosiere-Hottomont, O=Test Odoo SA, OU=R&D Accounting, ST=Wallonia</ds:X509IssuerName>
|
||||
<ds:X509SerialNumber>529783975559440394003723764779526236797615751643</ds:X509SerialNumber>
|
||||
</xades:IssuerSerial>
|
||||
</xades:Cert>
|
||||
</xades:SigningCertificate>
|
||||
<xades:SignaturePolicyIdentifier>
|
||||
<xades:SignaturePolicyId>
|
||||
<xades:SigPolicyId>
|
||||
<xades:Identifier>http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf</xades:Identifier>
|
||||
<xades:Description>Política de firma electrónica para facturación electrónica con formato Facturae</xades:Description>
|
||||
</xades:SigPolicyId>
|
||||
<xades:SigPolicyHash>
|
||||
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
|
||||
<ds:DigestValue>a/vDoueJq9zxWGCFwdsXdlXtkFSEek4XVtmlQCEDaE8=</ds:DigestValue>
|
||||
</xades:SigPolicyHash>
|
||||
<xades:SigPolicyQualifiers>
|
||||
<xades:SigPolicyQualifier>
|
||||
<xades:SPURI>http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf</xades:SPURI>
|
||||
</xades:SigPolicyQualifier>
|
||||
</xades:SigPolicyQualifiers>
|
||||
</xades:SignaturePolicyId>
|
||||
</xades:SignaturePolicyIdentifier>
|
||||
</xades:SignedSignatureProperties>
|
||||
<xades:SignedDataObjectProperties>
|
||||
<xades:DataObjectFormat ObjectReference="#Reference-Document-16e396210b43c90f11328fb78cefe178860e0dd7">
|
||||
<xades:Description/>
|
||||
<xades:ObjectIdentifier>
|
||||
<xades:Identifier Qualifier="OIDAsURN">urn:oid:1.2.840.10003.5.109.10</xades:Identifier>
|
||||
<xades:Description/>
|
||||
</xades:ObjectIdentifier>
|
||||
<xades:MimeType>text/xml</xades:MimeType>
|
||||
<xades:Encoding/>
|
||||
</xades:DataObjectFormat>
|
||||
</xades:SignedDataObjectProperties>
|
||||
</xades:SignedProperties>
|
||||
</xades:QualifyingProperties>
|
||||
</ds:Object>
|
||||
</ds:Signature>
|
||||
</fac:Facturae>
|
||||
@@ -8,7 +8,7 @@ from unittest.mock import patch
|
||||
import lxml
|
||||
from freezegun import freeze_time
|
||||
|
||||
from odoo import Command
|
||||
from odoo import Command, fields
|
||||
from odoo.addons.account_edi.tests.common import AccountEdiTestCommon
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.tests import tagged
|
||||
@@ -251,3 +251,94 @@ class TestEdiFacturaeXmls(AccountEdiTestCommon):
|
||||
|
||||
self.assertEqual(result['type'], 'ir.actions.act_url')
|
||||
self.assertEqual(invoice.invoice_line_ids[0].price_subtotal, 0.0)
|
||||
|
||||
def test_import_multiple_invoices(self):
|
||||
with file_open("l10n_es_edi_facturae/tests/data/import_multiple_invoices.xml", "rt") as f:
|
||||
imported_xml = lxml.etree.fromstring(f.read().encode())
|
||||
|
||||
moves = self.env['account.move'].create({'move_type': 'out_invoice'})
|
||||
moves._import_invoice_facturae(moves, {'xml_tree': imported_xml})
|
||||
|
||||
moves += self.env['account.move'].search([('ref', '=', 'INV/2023/00006'), ('company_id', '=', self.company_data['company'].id)], limit=1)
|
||||
|
||||
partner = self.env['res.partner'].search([
|
||||
('name', '=', 'Azure Interior'),
|
||||
('email', '=', 'azure.Interior24@example.com'),
|
||||
])
|
||||
currency = self.env['res.currency'].search([('name', '=', 'EUR')])
|
||||
|
||||
self.assertRecordValues(moves, [
|
||||
{
|
||||
'partner_id': partner.id,
|
||||
'amount_total': 2186.20,
|
||||
'amount_untaxed': 2119.0,
|
||||
'amount_tax': 67.2,
|
||||
'move_type': 'out_invoice',
|
||||
'currency_id': currency.id,
|
||||
'invoice_date': fields.Date.from_string('2023-08-01'),
|
||||
'invoice_date_due': fields.Date.from_string('2023-08-31'),
|
||||
'ref': 'INV/2023/00005',
|
||||
'narration': '<p>Terms and conditions.</p>',
|
||||
},
|
||||
{
|
||||
'partner_id': partner.id,
|
||||
'amount_total': 1161.60,
|
||||
'amount_untaxed': 960.0,
|
||||
'amount_tax': 201.60,
|
||||
'move_type': 'out_invoice',
|
||||
'currency_id': currency.id,
|
||||
'invoice_date': fields.Date.from_string('2023-07-01'),
|
||||
'invoice_date_due': fields.Date.from_string('2023-07-31'),
|
||||
'ref': 'INV/2023/00006',
|
||||
'narration': '<p>Legal References.</p>',
|
||||
},
|
||||
])
|
||||
|
||||
# Check first invoice's lines.
|
||||
self.assertRecordValues(moves[0].invoice_line_ids, [
|
||||
{
|
||||
'name': '[E-COM07] Large Cabinet',
|
||||
'price_unit': 320.0,
|
||||
'quantity': 1.0,
|
||||
'price_total': 387.2,
|
||||
'discount': 0.0,
|
||||
},
|
||||
{
|
||||
'name': '[E-COM09] Large Desk',
|
||||
'price_unit': 1799.0,
|
||||
'quantity': 1.0,
|
||||
'price_total': 1799,
|
||||
'discount': 0.0,
|
||||
}
|
||||
])
|
||||
|
||||
# Check second invoice's lines.
|
||||
self.assertRecordValues(moves[1].invoice_line_ids, [
|
||||
{
|
||||
'name': '[E-COM07] Large Cabinet',
|
||||
'price_unit': 320.0,
|
||||
'quantity': 3.0,
|
||||
'price_total': 1161.60,
|
||||
'discount': 0.0,
|
||||
},
|
||||
])
|
||||
|
||||
def test_import_withheld_taxes(self):
|
||||
with file_open("l10n_es_edi_facturae/tests/data/import_withholding_invoice.xml", "rt") as f:
|
||||
imported_xml = lxml.etree.fromstring(f.read().encode())
|
||||
|
||||
move = self.env['account.move'].create({'move_type': 'out_invoice'})
|
||||
move._import_invoice_facturae(move, {'xml_tree': imported_xml})
|
||||
|
||||
self.assertRecordValues(move, [
|
||||
{
|
||||
'amount_total': 323.2,
|
||||
'amount_untaxed': 320.0,
|
||||
'amount_tax': 3.2,
|
||||
},
|
||||
])
|
||||
|
||||
tax_amounts = [tax.amount for tax in move.invoice_line_ids.tax_ids]
|
||||
|
||||
# Check first invoice's lines.
|
||||
self.assertEqual(tax_amounts, [21.0, -20.0])
|
||||
|
||||
Reference in New Issue
Block a user