Add parser for UBL invoice and credit note
This commit is contained in:
@@ -1,22 +1,35 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
import bisect
|
||||
import datetime as dt
|
||||
import mimetypes
|
||||
import os
|
||||
from base64 import b64encode
|
||||
from itertools import groupby
|
||||
from base64 import b64decode, b64encode
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from itertools import chain, groupby
|
||||
from operator import itemgetter
|
||||
|
||||
import genshi
|
||||
import genshi.template
|
||||
# XXX fix: https://genshi.edgewall.org/ticket/582
|
||||
from genshi.template.astutil import ASTCodeGenerator, ASTTransformer
|
||||
from lxml import etree
|
||||
|
||||
from trytond.i18n import gettext
|
||||
from trytond.model import Model
|
||||
from trytond.pool import Pool
|
||||
from trytond.modules.product import round_price
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.rpc import RPC
|
||||
from trytond.tools import cached_property, slugify
|
||||
from trytond.transaction import Transaction
|
||||
|
||||
from .exceptions import InvoiceError
|
||||
from .party import ISO6523_TYPES
|
||||
|
||||
ISO6523 = {v: k for k, v in ISO6523_TYPES.items()}
|
||||
|
||||
if not hasattr(ASTCodeGenerator, 'visit_NameConstant'):
|
||||
def visit_NameConstant(self, node):
|
||||
if node.value is None:
|
||||
@@ -53,6 +66,7 @@ class Invoice(Model):
|
||||
super().__setup__()
|
||||
cls.__rpc__.update({
|
||||
'render': RPC(instantiate=0),
|
||||
'parse': RPC(readonly=False, result=int),
|
||||
})
|
||||
|
||||
def __init__(self, invoice):
|
||||
@@ -168,3 +182,873 @@ class Invoice(Model):
|
||||
@cached_property
|
||||
def lines(self):
|
||||
return [l for l in self.invoice.lines if l.type == 'line']
|
||||
|
||||
@classmethod
|
||||
def parse(cls, document):
|
||||
pool = Pool()
|
||||
Attachment = pool.get('ir.attachment')
|
||||
|
||||
tree = etree.parse(BytesIO(document))
|
||||
root = tree.getroot()
|
||||
namespace = root.nsmap.get(None)
|
||||
invoice, attachments = cls.parser(namespace)(root)
|
||||
invoice.save()
|
||||
invoice.update_taxes()
|
||||
cls.checker(namespace)(root, invoice)
|
||||
attachments = list(attachments)
|
||||
for attachment in attachments:
|
||||
attachment.resource = invoice
|
||||
Attachment.save(attachments)
|
||||
return invoice
|
||||
|
||||
@classmethod
|
||||
def parser(cls, namespace):
|
||||
return {
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2': (
|
||||
cls._parse_invoice_2),
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2': (
|
||||
cls._parse_credit_note_2),
|
||||
}.get(namespace)
|
||||
|
||||
@classmethod
|
||||
def checker(cls, namespace):
|
||||
return {
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2': (
|
||||
cls._check_invoice_2),
|
||||
'urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2': (
|
||||
cls._check_credit_note_2),
|
||||
}.get(namespace)
|
||||
|
||||
@classmethod
|
||||
def _parse_invoice_2(cls, root):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
Currency = pool.get('currency.currency')
|
||||
|
||||
type_code = root.findtext('./{*}InvoiceTypeCode')
|
||||
if type_code and type_code != '380':
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_invoice_type_code_unsupported',
|
||||
type_code=type_code))
|
||||
|
||||
invoice = Invoice(type='in')
|
||||
invoice.reference = root.findtext('./{*}ID')
|
||||
invoice.invoice_date = dt.date.fromisoformat(
|
||||
root.findtext('./{*}IssueDate'))
|
||||
invoice.party = cls._parse_2_supplier(
|
||||
root.find('./{*}AccountingSupplierParty'), create=True)
|
||||
invoice.set_journal()
|
||||
invoice.on_change_party()
|
||||
invoice.invoice_address = cls._parse_2_address(
|
||||
root.find(
|
||||
'./{*}AccountingSupplierParty/{*}Party/{*}PostalAddress'),
|
||||
party=invoice.party)
|
||||
invoice.party_tax_identifier = cls._parse_2_tax_identifier(
|
||||
root.findall(
|
||||
'./{*}AccountingSupplierParty/{*}Party/{*}PartyTaxScheme'),
|
||||
party=invoice.party, create=True)
|
||||
if (seller := root.find('./{*}SellerSupplierParty')) is not None:
|
||||
supplier = cls._parse_2_supplier(seller)
|
||||
else:
|
||||
supplier = invoice.party
|
||||
if (customer_party := root.find('./{*}AccountingCustomerParty')
|
||||
) is not None:
|
||||
invoice.company = cls._parse_2_company(customer_party)
|
||||
else:
|
||||
invoice.company = Invoice.default_company()
|
||||
if not invoice.company:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_company_not_found',
|
||||
company=etree.tostring(
|
||||
customer_party, pretty_print=True).decode()
|
||||
if customer_party else ''))
|
||||
|
||||
if (payee_party := root.find('./{*}PayeeParty')) is not None:
|
||||
party = cls._parse_2_party(payee_party)
|
||||
if not party:
|
||||
party = cls._create_2_party(payee_party)
|
||||
party.save()
|
||||
invoice.alternative_payees = [party]
|
||||
|
||||
currency_code = root.findtext('./{*}DocumentCurrencyCode')
|
||||
if not currency_code:
|
||||
payable_amount = root.find(
|
||||
'./{*}LegalMonetaryTotal/{*}PayableAmount')
|
||||
currency_code = payable_amount.get('currencyID')
|
||||
if currency_code is not None:
|
||||
try:
|
||||
invoice.currency, = Currency.search([
|
||||
('code', '=', currency_code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_currency_not_found',
|
||||
code=currency_code))
|
||||
|
||||
invoice.payment_term_date = cls._parse_2_payment_term_date(
|
||||
root.findall('./{*}PaymentTerms'))
|
||||
invoice.lines = [
|
||||
cls._parse_invoice_2_line(
|
||||
line, company=invoice.company, currency=invoice.currency,
|
||||
supplier=supplier)
|
||||
for line in root.iterfind('./{*}InvoiceLine')]
|
||||
invoice.taxes = [
|
||||
cls._parse_2_tax(
|
||||
tax, company=invoice.company)
|
||||
for tax in root.iterfind('./{*}TaxTotal/{*}TaxSubtotal')]
|
||||
|
||||
if (hasattr(Invoice, 'cash_rounding')
|
||||
and (root.find(
|
||||
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
|
||||
is not None)):
|
||||
invoice.cash_rounding = True
|
||||
return invoice, cls._parse_2_attachments(root)
|
||||
|
||||
@classmethod
|
||||
def _parse_invoice_2_line(
|
||||
cls, invoice_line, company, currency, supplier=None):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.invoice.line')
|
||||
UoM = pool.get('product.uom')
|
||||
Tax = pool.get('account.tax')
|
||||
AccountConfiguration = pool.get('account.configuration')
|
||||
|
||||
account_configuration = AccountConfiguration(1)
|
||||
|
||||
line = Line(type='line', company=company, currency=currency)
|
||||
if (invoiced_quantity := invoice_line.find('./{*}InvoicedQuantity')
|
||||
) is not None:
|
||||
line.quantity = float(invoiced_quantity.text)
|
||||
if (unit_code := invoiced_quantity.get('unitCode')) is not None:
|
||||
try:
|
||||
line.unit, = UoM.search([
|
||||
('unece_code', '=', unit_code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_unit_not_found',
|
||||
code=unit_code))
|
||||
else:
|
||||
line.quantity = 1
|
||||
line.unit = None
|
||||
|
||||
line.product = cls._parse_2_item(
|
||||
invoice_line.find('./{*}Item'), supplier=supplier)
|
||||
if line.product:
|
||||
line.on_change_product()
|
||||
|
||||
line.description = '\n'.join(e.text for e in chain(
|
||||
invoice_line.iterfind('./{*}Item/{*}Name'),
|
||||
invoice_line.iterfind('./{*}Item/{*}BrandName'),
|
||||
invoice_line.iterfind('./{*}Item/{*}ModelName'),
|
||||
invoice_line.iterfind('./{*}Item/{*}Description'),
|
||||
invoice_line.iterfind(
|
||||
'./{*}Item/{*}AdditionalInformation'),
|
||||
invoice_line.iterfind('./{*}Item/{*}WarrantyInformation'),
|
||||
) if e is not None and e.text)
|
||||
|
||||
if not line.product:
|
||||
if line.description:
|
||||
similar_lines = Line.search([
|
||||
('description', 'ilike', line.description),
|
||||
('invoice.company', '=', company),
|
||||
('invoice.type', '=', 'in'),
|
||||
('invoice.state', 'in',
|
||||
['validated', 'posted', 'paid']),
|
||||
],
|
||||
order=[('invoice.invoice_date', 'DESC')],
|
||||
limit=1)
|
||||
else:
|
||||
similar_lines = []
|
||||
if similar_lines:
|
||||
similar_line, = similar_lines
|
||||
line.account = similar_line.account
|
||||
line.product = similar_line.product
|
||||
else:
|
||||
line.account = account_configuration.get_multivalue(
|
||||
'default_category_account_expense',
|
||||
company=company.id)
|
||||
|
||||
for line_reference in invoice_line.iterfind('./{*}OrderLineReference'):
|
||||
line.origin = cls._parse_2_line_reference(
|
||||
line_reference, line, company, supplier=supplier)
|
||||
if line.origin:
|
||||
break
|
||||
|
||||
if (price_amount := invoice_line.findtext('./{*}Price/{*}PriceAmount')
|
||||
) is not None:
|
||||
line.unit_price = round_price(Decimal(price_amount))
|
||||
else:
|
||||
line.unit_price = round_price(
|
||||
Decimal(invoice_line.findtext('./{*}LineExtensionAmount'))
|
||||
/ Decimal(str(line.quantity)))
|
||||
|
||||
if invoice_line.find('./{*}Item/{*}ClassifiedTaxCategory') is not None:
|
||||
tax_categories = invoice_line.iterfind(
|
||||
'./{*}Item/{*}ClassifiedTaxCategory')
|
||||
else:
|
||||
tax_categories = invoice_line.iterfind(
|
||||
'./{*}TaxTotal/{*}TaxSubtotal/{*}TaxCategory')
|
||||
taxes = []
|
||||
for tax_category in tax_categories:
|
||||
domain = cls._parse_2_tax_category(tax_category)
|
||||
domain.extend([
|
||||
['OR',
|
||||
('group', '=', None),
|
||||
('group.kind', 'in', ['purchase', 'both']),
|
||||
],
|
||||
('company', '=', company.id),
|
||||
])
|
||||
try:
|
||||
tax, = Tax.search(domain, limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_tax_not_found',
|
||||
tax_category=etree.tostring(
|
||||
tax_category, pretty_print=True).decode()))
|
||||
taxes.append(tax)
|
||||
line.taxes = taxes
|
||||
return line
|
||||
|
||||
@classmethod
|
||||
def _parse_credit_note_2(cls, root):
|
||||
pool = Pool()
|
||||
Invoice = pool.get('account.invoice')
|
||||
Currency = pool.get('currency.currency')
|
||||
|
||||
type_code = root.findtext('./{*}CreditNoteTypeCode')
|
||||
if type_code and type_code != '381':
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_credit_note_type_code_unsupported',
|
||||
type_code=type_code))
|
||||
|
||||
invoice = Invoice(type='in')
|
||||
invoice.reference = root.findtext('./{*}ID')
|
||||
invoice.invoice_date = dt.date.fromisoformat(
|
||||
root.findtext('./{*}IssueDate'))
|
||||
invoice.party = cls._parse_2_supplier(
|
||||
root.find('./{*}AccountingSupplierParty'), create=True)
|
||||
invoice.set_journal()
|
||||
invoice.on_change_party()
|
||||
if (seller := root.find('./{*}SellerSupplierParty')) is not None:
|
||||
supplier = cls._parse_2_supplier(seller)
|
||||
else:
|
||||
supplier = invoice.party
|
||||
if (customer_party := root.find('./{*}AccountingCustomerParty')
|
||||
) is not None:
|
||||
invoice.company = cls._parse_2_company(customer_party)
|
||||
else:
|
||||
invoice.company = Invoice.default_company()
|
||||
if (payee_party := root.find('./{*}PayeeParty')) is not None:
|
||||
party = cls._parse_2_party(payee_party)
|
||||
if not party:
|
||||
party = cls._create_2_party(payee_party)
|
||||
party.save()
|
||||
invoice.alternative_payees = [party]
|
||||
if (currency_code := root.findtext('./{*}DocumentCurrencyCode')
|
||||
) is not None:
|
||||
try:
|
||||
invoice.currency, = Currency.search([
|
||||
('code', '=', currency_code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_currency_not_found',
|
||||
code=currency_code))
|
||||
invoice.payment_term_date = cls._parse_2_payment_term_date(
|
||||
root.findall('./{*}PaymentTerms'))
|
||||
invoice.lines = [
|
||||
cls._parse_credit_note_2_line(
|
||||
line, company=invoice.company, currency=invoice.currency,
|
||||
supplier=supplier)
|
||||
for line in root.iterfind('./{*}CreditNoteLine')]
|
||||
invoice.taxes = [
|
||||
cls._parse_2_tax(
|
||||
tax, company=invoice.company)
|
||||
for tax in root.iterfind('./{*}TaxTotal/{*}TaxSubtotal')]
|
||||
|
||||
if (hasattr(Invoice, 'cash_rounding')
|
||||
and root.find(
|
||||
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
|
||||
is not None):
|
||||
invoice.cash_rounding = True
|
||||
return invoice, cls._parse_2_attachments(root)
|
||||
|
||||
@classmethod
|
||||
def _parse_credit_note_2_line(
|
||||
cls, credit_note_line, company, currency, supplier=None):
|
||||
pool = Pool()
|
||||
Line = pool.get('account.invoice.line')
|
||||
UoM = pool.get('product.uom')
|
||||
Tax = pool.get('account.tax')
|
||||
AccountConfiguration = pool.get('account.configuration')
|
||||
|
||||
account_configuration = AccountConfiguration(1)
|
||||
|
||||
line = Line(type='line', company=company, currency=currency)
|
||||
if (credited_quantity := credit_note_line.find('./{*}CreditedQuantity')
|
||||
) is not None:
|
||||
line.quantity = -float(credited_quantity.text)
|
||||
if (unit_code := credited_quantity.get('unitCode')) is not None:
|
||||
try:
|
||||
line.unit, = UoM.search([
|
||||
('unece_code', '=', unit_code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_unit_not_found',
|
||||
code=unit_code))
|
||||
else:
|
||||
line.quantity = -1
|
||||
line.unit = None
|
||||
|
||||
line.product = cls._parse_2_item(
|
||||
credit_note_line.find('./{*}Item'), supplier=supplier)
|
||||
if line.product:
|
||||
line.on_change_product()
|
||||
|
||||
line.description = '\n'.join(e.text for e in chain(
|
||||
credit_note_line.iterfind('./{*}Item/{*}Name'),
|
||||
credit_note_line.iterfind('./{*}Item/{*}BrandName'),
|
||||
credit_note_line.iterfind('./{*}Item/{*}ModelName'),
|
||||
credit_note_line.iterfind('./{*}Item/{*}Description'),
|
||||
credit_note_line.iterfind(
|
||||
'./{*}Item/{*}AdditionalInformation'),
|
||||
credit_note_line.iterfind('./{*}Item/{*}WarrantyInformation'),
|
||||
) if e is not None and e.text)
|
||||
|
||||
if not line.product:
|
||||
if line.description:
|
||||
similar_lines = Line.search([
|
||||
('description', 'ilike', line.description),
|
||||
('invoice.company', '=', company),
|
||||
('invoice.type', '=', 'in'),
|
||||
('invoice.state', 'in',
|
||||
['validated', 'posted', 'paid']),
|
||||
],
|
||||
order=[('invoice.invoice_date', 'DESC')],
|
||||
limit=1)
|
||||
else:
|
||||
similar_lines = []
|
||||
if similar_lines:
|
||||
similar_line, = similar_lines
|
||||
line.account = similar_line.account
|
||||
line.product = similar_line.product
|
||||
else:
|
||||
line.account = account_configuration.get_multivalue(
|
||||
'default_category_account_expense',
|
||||
company=company.id)
|
||||
|
||||
for line_reference in credit_note_line.iterfind(
|
||||
'./{*}OrderLineReference'):
|
||||
line.origin = cls._parse_2_line_reference(
|
||||
line_reference, line, company, supplier=supplier)
|
||||
if line.origin:
|
||||
break
|
||||
|
||||
if (price_amount := credit_note_line.findtext(
|
||||
'./{*}Price/{*}PriceAmount')) is not None:
|
||||
line.unit_price = round_price(Decimal(price_amount))
|
||||
else:
|
||||
line.unit_price = round_price(
|
||||
Decimal(credit_note_line.findtext('./{*}LineExtensionAmount'))
|
||||
/ line.quantity)
|
||||
|
||||
if (credit_note_line.find('./{*}Item/{*}ClassifiedTaxCategory')
|
||||
is not None):
|
||||
tax_categories = credit_note_line.iterfind(
|
||||
'./{*}Item/{*}ClassifiedTaxCategory')
|
||||
else:
|
||||
tax_categories = credit_note_line.iterfind(
|
||||
'./{*}TaxTotal/{*}TaxSubtotal/{*}TaxCategory')
|
||||
taxes = []
|
||||
for tax_category in tax_categories:
|
||||
domain = cls._parse_2_tax_category(tax_category)
|
||||
domain.extend([
|
||||
['OR',
|
||||
('group', '=', None),
|
||||
('group.kind', 'in', ['purchase', 'both']),
|
||||
],
|
||||
('company', '=', company.id),
|
||||
])
|
||||
try:
|
||||
tax, = Tax.search(domain, limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_tax_not_found',
|
||||
tax_category=etree.tostring(
|
||||
tax_category, pretty_print=True).decode()))
|
||||
taxes.append(tax)
|
||||
line.taxes = taxes
|
||||
return line
|
||||
|
||||
@classmethod
|
||||
def _parse_2_supplier(cls, supplier_party, create=False):
|
||||
pool = Pool()
|
||||
Party = pool.get('party.party')
|
||||
for account_id in filter(None, chain(
|
||||
[supplier_party.find('./{*}CustomerAssignedAccountID')],
|
||||
supplier_party.iterfind('./{*}AdditionalAccountID'))):
|
||||
if account_id.text:
|
||||
try:
|
||||
party, = Party.search([
|
||||
('code', '=', account_id.text),
|
||||
])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return party
|
||||
party_el = supplier_party.find('./{*}Party')
|
||||
party = cls._parse_2_party(party_el)
|
||||
if not party and create:
|
||||
party = cls._create_2_party(party_el)
|
||||
party.save()
|
||||
return party
|
||||
|
||||
@classmethod
|
||||
def _parse_2_party(cls, party_el):
|
||||
pool = Pool()
|
||||
Party = pool.get('party.party')
|
||||
|
||||
for identifier in party_el.iterfind('./{*}PartyIdentification/{*}ID'):
|
||||
if identifier.text:
|
||||
parties = Party.search([
|
||||
('identifiers.code', '=', identifier.text),
|
||||
])
|
||||
if len(parties) == 1:
|
||||
party, = parties
|
||||
return party
|
||||
|
||||
@classmethod
|
||||
def _create_2_party(cls, party_el):
|
||||
pool = Pool()
|
||||
Party = pool.get('party.party')
|
||||
party = Party()
|
||||
party.name = party_el.findtext('./{*}PartyName/{*}Name')
|
||||
identifiers = []
|
||||
for identifier in party_el.iterfind('./{*}PartyIdentification/{*}ID'):
|
||||
if identifier.text:
|
||||
identifiers.append(cls._create_2_party_identifier(
|
||||
identifier))
|
||||
party.identifiers = identifiers
|
||||
if (address := party_el.find('./{*}PostalAddress')
|
||||
) is not None:
|
||||
party.addresses = [cls._create_2_address(address)]
|
||||
return party
|
||||
|
||||
@classmethod
|
||||
def _create_2_party_identifier(cls, identifier):
|
||||
pool = Pool()
|
||||
Identifier = pool.get('party.identifier')
|
||||
if schemeId := identifier.get('schemeID'):
|
||||
type = ISO6523.get(schemeId)
|
||||
else:
|
||||
type = None
|
||||
return Identifier(type=type, code=identifier.text)
|
||||
|
||||
@classmethod
|
||||
def _parse_2_address(cls, address_el, party):
|
||||
pool = Pool()
|
||||
Address = pool.get('party.address')
|
||||
|
||||
address = cls._create_2_address(address_el)
|
||||
|
||||
domain = [('party', '=', party)]
|
||||
for fname in Address._fields:
|
||||
if value := getattr(address, fname, None):
|
||||
domain.append((fname, '=', value))
|
||||
try:
|
||||
address, = Address.search(domain, limit=1)
|
||||
except ValueError:
|
||||
address.party = party
|
||||
address.save()
|
||||
return address
|
||||
|
||||
@classmethod
|
||||
def _create_2_address(cls, address_el):
|
||||
pool = Pool()
|
||||
Address = pool.get('party.address')
|
||||
Country = pool.get('country.country')
|
||||
|
||||
address = Address()
|
||||
|
||||
if address_el is None:
|
||||
return address
|
||||
|
||||
address.post_box = address_el.findtext('./{*}Postbox')
|
||||
address.floor_number = address_el.findtext('./{*}Floor')
|
||||
address.room_number = address_el.findtext('./{*}Room')
|
||||
address.street_name = address_el.findtext('./{*}StreetName')
|
||||
address.building_name = address_el.findtext('./{*}BuildingName')
|
||||
address.building_number = address_el.findtext('./{*}BuildingNumber')
|
||||
address.city = address_el.findtext('./{*}CityName')
|
||||
address.postal_code = address_el.findtext('./{*}PostalZone')
|
||||
if (country_code := address_el.findtext(
|
||||
'./{*}Country/{*}IdentificationCode[@listId="ISO3166-1"]')
|
||||
) is not None:
|
||||
try:
|
||||
country, = Country.search([
|
||||
('code', '=', country_code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
address.country = country
|
||||
address.street_unstructured = '\n'.join(
|
||||
(line.text
|
||||
for line in address_el.iterfind('./{*}AddressLine/{*}Line')))
|
||||
return address
|
||||
|
||||
@classmethod
|
||||
def _parse_2_tax_identifier(cls, party_tax_schemes, party, create=False):
|
||||
pool = Pool()
|
||||
Identifier = pool.get('party.identifier')
|
||||
|
||||
tax_identifier_types = party.tax_identifier_types()
|
||||
|
||||
for party_tax_scheme in party_tax_schemes:
|
||||
company_id = party_tax_scheme.find('./{*}CompanyID')
|
||||
if company_id is not None:
|
||||
scheme_id = company_id.get('schemeID')
|
||||
value = company_id.text
|
||||
|
||||
for identifier in party.identifiers:
|
||||
if (identifier.type in tax_identifier_types
|
||||
and identifier.iso_6523 == scheme_id
|
||||
and identifier.code == value):
|
||||
return identifier
|
||||
else:
|
||||
if create and scheme_id in ISO6523:
|
||||
identifier = Identifier(
|
||||
party=party,
|
||||
type=ISO6523[scheme_id],
|
||||
code=value)
|
||||
identifier.save()
|
||||
return identifier
|
||||
|
||||
@classmethod
|
||||
def _parse_2_company(cls, customer_party):
|
||||
pool = Pool()
|
||||
Company = pool.get('company.company')
|
||||
try:
|
||||
CustomerCode = pool.get('party.party.customer_code')
|
||||
except KeyError:
|
||||
CustomerCode = None
|
||||
|
||||
if CustomerCode:
|
||||
for account_id in filter(None, chain(
|
||||
[customer_party.find('./{*}CustomerAssignedAccountID'),
|
||||
customer_party.find(
|
||||
'./{*}SupplierAssignedAccountID')],
|
||||
customer_party.iterfind('./{*}AdditionalAccountID'))):
|
||||
if account_id.text:
|
||||
try:
|
||||
customer_code, = CustomerCode.search([
|
||||
('customer_code', '=', account_id.text),
|
||||
])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return customer_code.company
|
||||
for identifier in customer_party.iterfind(
|
||||
'./{*}Party/{*}PartyIdentification/{*}ID'):
|
||||
if identifier.text:
|
||||
companies = Company.search([
|
||||
('party.identifiers.code', '=', identifier.text),
|
||||
])
|
||||
if len(companies) == 1:
|
||||
company, = companies
|
||||
return company
|
||||
|
||||
for company_id in customer_party.iterfind(
|
||||
'./{*}Party/{*}PartyTaxScheme/{*}CompanyID'):
|
||||
companies = Company.search([
|
||||
('party.identifiers.code', '=', company_id.text),
|
||||
])
|
||||
if len(companies) == 1:
|
||||
company, = companies
|
||||
return company
|
||||
|
||||
for name in chain(
|
||||
customer_party.iterfind(
|
||||
'./{*}Party/{*}PartyTaxScheme/{*}RegistrationName'),
|
||||
customer_party.iterfind('./{*}Party/{*}PartyName/{*}Name'),
|
||||
):
|
||||
if name is None:
|
||||
continue
|
||||
companies = Company.search([
|
||||
('party.name', '=', name.text),
|
||||
])
|
||||
if len(companies) == 1:
|
||||
company, = companies
|
||||
return company
|
||||
|
||||
@classmethod
|
||||
def _parse_2_payment_term_date(cls, payment_terms):
|
||||
dates = []
|
||||
for payment_term in payment_terms:
|
||||
if (date := payment_term.findtext('./{*}PaymentDueDate')
|
||||
) is not None:
|
||||
dates.append(dt.date.fromisoformat(date))
|
||||
return min(dates, default=None)
|
||||
|
||||
@classmethod
|
||||
def _parse_2_item(cls, item, supplier=None):
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
|
||||
if (identifier := item.find('./{*}StandardItemIdentification/{*}ID')
|
||||
) is not None:
|
||||
if identifier.get('schemeID') == 'GTIN':
|
||||
try:
|
||||
product, = Product.search([
|
||||
('identifiers', 'where', [
|
||||
('type', 'in', ['ean', 'isbn', 'ismn']),
|
||||
('code', '=', identifier.text),
|
||||
]),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return product
|
||||
if (code := item.findtext('./{*}BuyersItemIdentification/{*}ID')
|
||||
) is not None:
|
||||
try:
|
||||
product, = Product.search([
|
||||
('code', '=', code),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
return product
|
||||
|
||||
@classmethod
|
||||
def _parse_2_line_reference(
|
||||
cls, line_reference, line, company, supplier=None):
|
||||
return
|
||||
|
||||
@classmethod
|
||||
def _parse_2_tax_category(cls, tax_category):
|
||||
domain = [
|
||||
('parent', '=', None),
|
||||
]
|
||||
if (unece_category_code := tax_category.findtext('./{*}ID')
|
||||
) is not None:
|
||||
domain.append(('unece_category_code', '=', unece_category_code))
|
||||
if (unece_code := tax_category.findtext('./{*}TaxScheme/{*}ID')
|
||||
) is not None:
|
||||
domain.append(('unece_code', '=', unece_code))
|
||||
percent = tax_category.findtext('./{*}Percent')
|
||||
if percent:
|
||||
domain.append(('type', '=', 'percentage'))
|
||||
domain.append(('rate', '=', Decimal(percent) / 100))
|
||||
return domain
|
||||
|
||||
@classmethod
|
||||
def _parse_2_tax(cls, tax, company):
|
||||
pool = Pool()
|
||||
Tax = pool.get('account.tax')
|
||||
InvoiceTax = pool.get('account.invoice.tax')
|
||||
|
||||
invoice_tax = InvoiceTax(manual=False)
|
||||
|
||||
tax_category = tax.find('./{*}TaxCategory')
|
||||
domain = cls._parse_2_tax_category(tax_category)
|
||||
domain.extend([
|
||||
['OR',
|
||||
('group', '=', None),
|
||||
('group.kind', 'in', ['purchase', 'both']),
|
||||
],
|
||||
('company', '=', company.id),
|
||||
])
|
||||
try:
|
||||
invoice_tax.tax, = Tax.search(domain, limit=1)
|
||||
except ValueError:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_tax_not_found',
|
||||
tax_category=etree.tostring(
|
||||
tax_category, pretty_print=True).decode()))
|
||||
|
||||
invoice_tax.amount = Decimal(tax.findtext('./{*}TaxAmount'))
|
||||
if (taxable_amount := tax.findtext('./{*}TaxableAmount')) is not None:
|
||||
invoice_tax.base = Decimal(taxable_amount)
|
||||
else:
|
||||
# Use tax amount to define the sign of unknown base
|
||||
invoice_tax.base = invoice_tax.amount
|
||||
|
||||
invoice_tax.on_change_tax()
|
||||
|
||||
return invoice_tax
|
||||
|
||||
@classmethod
|
||||
def _parse_2_attachments(cls, root):
|
||||
pool = Pool()
|
||||
Attachment = pool.get('ir.attachment')
|
||||
|
||||
for name in [
|
||||
'DespatchDocumentReference',
|
||||
'ReceiptDocumentReference',
|
||||
'ContractDocumentReference',
|
||||
'AdditionalDocumentReference',
|
||||
'StatementDocumentReference',
|
||||
'OriginatorDocumentReference'
|
||||
]:
|
||||
for document in root.iterfind(f'./{{*}}{name}'):
|
||||
attachment = Attachment()
|
||||
name = ' '.join(filter(None, [
|
||||
document.findtext('./{*}DocumentType'),
|
||||
document.findtext('./{*}ID'),
|
||||
]))
|
||||
if (data := document.find(
|
||||
'./{*}Attachment/{*}EmbeddedDocumentBinaryObject')
|
||||
) is not None:
|
||||
mime_code = (
|
||||
data.get('mimeCode') or 'application/octet-stream')
|
||||
name += mimetypes.guess_extension(mime_code) or ''
|
||||
attachment.type = 'data'
|
||||
data = b64decode(data.text)
|
||||
attachment.data = data
|
||||
elif data := document.findtext(
|
||||
'./{*}Attachment/{*}EmbeddedDocument'):
|
||||
name += '.txt'
|
||||
attachment.type = 'data'
|
||||
attachment.data = data
|
||||
elif url := document.findtext(
|
||||
'./{*}Attachment/{*}ExternalReference/{*}URI'):
|
||||
attachment.type = 'link'
|
||||
Attachment.link = url
|
||||
attachment.name = name
|
||||
yield attachment
|
||||
|
||||
@classmethod
|
||||
def _check_invoice_2(cls, root, invoice):
|
||||
pool = Pool()
|
||||
Lang = pool.get('ir.lang')
|
||||
lang = Lang.get()
|
||||
|
||||
payable_amount = Decimal(
|
||||
root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
|
||||
prepaid_amount = Decimal(
|
||||
root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
|
||||
or 0)
|
||||
amount = payable_amount + prepaid_amount
|
||||
if not getattr(invoice, 'cash_rounding', False):
|
||||
payable_rounding_amount = Decimal(
|
||||
root.findtext(
|
||||
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
|
||||
or 0)
|
||||
amount -= payable_rounding_amount
|
||||
if invoice.total_amount != amount:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_invoice_total_amount_different',
|
||||
invoice=invoice.rec_name,
|
||||
total_amount=lang.format_number(invoice.total_amount),
|
||||
amount=lang.format_number(amount)))
|
||||
|
||||
tax_total = sum(Decimal(amount.text) for amount in root.iterfind(
|
||||
'./{*}TaxTotal/{*}TaxAmount'))
|
||||
if invoice.tax_amount != tax_total:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_invoice_tax_amount_different',
|
||||
invoice=invoice.rec_name,
|
||||
tax_amount=lang.format_number(invoice.tax_amount),
|
||||
tax_total=lang.format_number(tax_total)))
|
||||
|
||||
@classmethod
|
||||
def _check_credit_note_2(cls, root, invoice):
|
||||
pool = Pool()
|
||||
Lang = pool.get('ir.lang')
|
||||
lang = Lang.get()
|
||||
|
||||
payable_amount = Decimal(
|
||||
root.findtext('./{*}LegalMonetaryTotal/{*}PayableAmount'))
|
||||
prepaid_amount = Decimal(
|
||||
root.findtext('./{*}LegalMonetaryTotal/{*}PrepaidAmount')
|
||||
or 0)
|
||||
amount = payable_amount + prepaid_amount
|
||||
if not getattr(invoice, 'cash_rounding', False):
|
||||
payable_rounding_amount = Decimal(
|
||||
root.findtext(
|
||||
'./{*}LegalMonetaryTotal/{*}PayableRoundingAmount')
|
||||
or 0)
|
||||
amount -= payable_rounding_amount
|
||||
if -invoice.total_amount != amount:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_invoice_total_amount_different',
|
||||
invoice=invoice.rec_name,
|
||||
total_amount=lang.format_number(-invoice.total_amount),
|
||||
amount=lang.format_number(amount)))
|
||||
|
||||
tax_total = sum(Decimal(amount.text) for amount in root.iterfind(
|
||||
'./{*}TaxTotal/{*}TaxAmount'))
|
||||
if -invoice.tax_amount != tax_total:
|
||||
raise InvoiceError(gettext(
|
||||
'edocument_ubl.msg_invoice_tax_amount_different',
|
||||
invoice=invoice.rec_name,
|
||||
tax_amount=lang.format_number(-invoice.tax_amount),
|
||||
tax_total=lang.format_number(tax_total)))
|
||||
|
||||
|
||||
class Invoice_Purchase(metaclass=PoolMeta):
|
||||
__name__ = 'edocument.ubl.invoice'
|
||||
|
||||
@classmethod
|
||||
def _parse_2_item(cls, item, supplier=None):
|
||||
pool = Pool()
|
||||
Product = pool.get('product.product')
|
||||
|
||||
product = super()._parse_2_item(item, supplier=supplier)
|
||||
|
||||
if (not product
|
||||
and supplier
|
||||
and (code := item.findtext(
|
||||
'./{*}SellersItemIdentification/{*}ID'))):
|
||||
try:
|
||||
product, = Product.search([
|
||||
('product_suppliers', 'where', [
|
||||
('party', '=', supplier.id),
|
||||
('code', '=', code),
|
||||
]),
|
||||
], limit=1)
|
||||
except ValueError:
|
||||
pass
|
||||
return product
|
||||
|
||||
@classmethod
|
||||
def _parse_2_line_reference(
|
||||
cls, line_reference, line, company, supplier=None):
|
||||
pool = Pool()
|
||||
PurchaseLine = pool.get('purchase.line')
|
||||
UoM = pool.get('product.uom')
|
||||
|
||||
origin = super()._parse_2_item_reference(
|
||||
line_reference, line, supplier=supplier)
|
||||
if origin:
|
||||
return origin
|
||||
if not line or not line.product or not line.unit:
|
||||
return
|
||||
|
||||
if numbers := list(filter(None, [
|
||||
line_reference.findtext('./{*}OrderReference/{*}ID'),
|
||||
line_reference.findtext(
|
||||
'./{*}OrderReference/{*}SalesOrderID'),
|
||||
])):
|
||||
purchase_lines = PurchaseLine.search([
|
||||
('purchase.company', '=', company),
|
||||
('purchase.rec_name', 'in', numbers),
|
||||
('type', '=', 'line'),
|
||||
('product', '=', line.product),
|
||||
])
|
||||
if purchase_lines:
|
||||
quantities = []
|
||||
for purchase_line in purchase_lines:
|
||||
quantity = UoM.compute_qty(
|
||||
purchase_line.unit, purchase_line.quantity, line.unit)
|
||||
quantities.append((quantity, purchase_line))
|
||||
key = itemgetter(0)
|
||||
quantities.sort(key=key)
|
||||
index = bisect.bisect_left(quantities, line.quantity, key=key)
|
||||
if index >= len(quantities):
|
||||
index = -1
|
||||
origin = quantities[index][1]
|
||||
return origin
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.exceptions import UserError
|
||||
|
||||
|
||||
class InvoiceError(UserError):
|
||||
pass
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<tryton>
|
||||
<data grouped="1">
|
||||
<record model="ir.message" id="msg_invoice_type_code_unsupported">
|
||||
<field name="text">Unsupported invoice type code "%(type_code)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_credit_note_type_code_unsupported">
|
||||
<field name="text">Unsupported credit note type code "%(type_code)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_company_not_found">
|
||||
<field name="text">Could not find the company for:
|
||||
%(company)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_currency_not_found">
|
||||
<field name="text">Could not find a currency with code "%(code)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_unit_not_found">
|
||||
<field name="text">Could not find a unit with UNECE code "%(code)s".</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_tax_not_found">
|
||||
<field name="text">Could not find tax for:
|
||||
%(tax_category)s</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_total_amount_different">
|
||||
<field name="text">The total amount %(total_amount)s of the invoice "%(invoice)s" is different from the amount %(amount)s.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_tax_amount_different">
|
||||
<field name="text">The tax amount %(tax_amount)s of the invoice "%(invoice)s" is different from the tax total %(tax_total)s.</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
@@ -44,15 +44,17 @@ else:
|
||||
download_url = 'http://downloads.tryton.org/%s.%s/' % (
|
||||
major_version, minor_version)
|
||||
|
||||
requires = ['Genshi']
|
||||
requires = ['Genshi', 'lxml']
|
||||
for dep in info.get('depends', []):
|
||||
if not re.match(r'(ir|res)(\W|$)', dep):
|
||||
requires.append(get_require_version('trytond_%s' % dep))
|
||||
requires.append(get_require_version('trytond'))
|
||||
|
||||
tests_require = [
|
||||
'lxml',
|
||||
get_require_version('proteus'),
|
||||
get_require_version('trytond_account_cash_rounding'),
|
||||
get_require_version('trytond_account_invoice'),
|
||||
get_require_version('trytond_purchase'),
|
||||
]
|
||||
|
||||
setup(name=name,
|
||||
@@ -81,7 +83,7 @@ setup(name=name,
|
||||
'trytond.modules.edocument_ubl': (info.get('xml', [])
|
||||
+ ['tryton.cfg', 'view/*.xml', 'locale/*.po', '*.fodt',
|
||||
'icons/*.svg', 'template/*/*.xml', 'tests/*/*/*.xsq',
|
||||
'tests/*.rst', 'tests/*.json']),
|
||||
'tests/*.rst', 'tests/*.json', 'tests/*.xml']),
|
||||
},
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CreditNote xmlns="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
||||
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
|
||||
<cbc:ID>TOSL108</cbc:ID>
|
||||
<cbc:IssueDate>2009-12-15</cbc:IssueDate>
|
||||
<cbc:Note languageID="en">Ordered in our booth at the convention.</cbc:Note>
|
||||
<cbc:DocumentCurrencyCode listID="ISO 4217 Alpha" listAgencyID="6">EUR</cbc:DocumentCurrencyCode>
|
||||
<cbc:AccountingCost>Project cost code 123</cbc:AccountingCost>
|
||||
<cac:InvoicePeriod>
|
||||
<cbc:StartDate>2009-11-01</cbc:StartDate>
|
||||
<cbc:EndDate>2009-11-30</cbc:EndDate>
|
||||
</cac:InvoicePeriod>
|
||||
<cac:OrderReference>
|
||||
<cbc:ID>123</cbc:ID>
|
||||
</cac:OrderReference>
|
||||
<cac:ContractDocumentReference>
|
||||
<cbc:ID>Contract321</cbc:ID>
|
||||
<cbc:DocumentType>Framework agreement</cbc:DocumentType>
|
||||
</cac:ContractDocumentReference>
|
||||
<cac:AdditionalDocumentReference>
|
||||
<cbc:ID>Doc1</cbc:ID>
|
||||
<cbc:DocumentType>Timesheet</cbc:DocumentType>
|
||||
<cac:Attachment>
|
||||
<cac:ExternalReference>
|
||||
<cbc:URI>http://www.suppliersite.eu/sheet001.html</cbc:URI>
|
||||
</cac:ExternalReference>
|
||||
</cac:Attachment>
|
||||
</cac:AdditionalDocumentReference>
|
||||
<cac:AdditionalDocumentReference>
|
||||
<cbc:ID>Doc2</cbc:ID>
|
||||
<cbc:DocumentType>Drawing</cbc:DocumentType>
|
||||
<cac:Attachment>
|
||||
<cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf">UjBsR09EbGhjZ0dTQUxNQUFBUUNBRU1tQ1p0dU1GUXhEUzhi</cbc:EmbeddedDocumentBinaryObject>
|
||||
</cac:Attachment>
|
||||
</cac:AdditionalDocumentReference>
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="GLN" schemeAgencyID="9">1234567890123</cbc:EndpointID>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="ZZZ">Supp123</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Salescompany ltd.</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">1231412341324</cbc:ID>
|
||||
<cbc:Postbox>5467</cbc:Postbox>
|
||||
<cbc:StreetName>Main street</cbc:StreetName>
|
||||
<cbc:AdditionalStreetName>Suite 123</cbc:AdditionalStreetName>
|
||||
<cbc:BuildingNumber>1</cbc:BuildingNumber>
|
||||
<cbc:Department>Revenue department</cbc:Department>
|
||||
<cbc:CityName>Big city</cbc:CityName>
|
||||
<cbc:PostalZone>54321</cbc:PostalZone>
|
||||
<cbc:CountrySubentityCode>RegionA</cbc:CountrySubentityCode>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode listID="ISO3166-1" listAgencyID="6">DK</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID schemeID="DKVAT" schemeAgencyID="ZZZ">DK12345</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>The Sellercompany Incorporated</cbc:RegistrationName>
|
||||
<cbc:CompanyID schemeID="CVR" schemeAgencyID="ZZZ">5402697509</cbc:CompanyID>
|
||||
<cac:RegistrationAddress>
|
||||
<cbc:CityName>Big city</cbc:CityName>
|
||||
<cbc:CountrySubentity>RegionA</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DK</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:RegistrationAddress>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Telephone>4621230</cbc:Telephone>
|
||||
<cbc:Telefax>4621231</cbc:Telefax>
|
||||
<cbc:ElectronicMail>antonio@salescompany.dk</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
<cac:Person>
|
||||
<cbc:FirstName>Antonio</cbc:FirstName>
|
||||
<cbc:FamilyName>M</cbc:FamilyName>
|
||||
<cbc:MiddleName>Salemacher</cbc:MiddleName>
|
||||
<cbc:JobTitle>Sales manager</cbc:JobTitle>
|
||||
</cac:Person>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:AccountingCustomerParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="GLN" schemeAgencyID="9">1234567987654</cbc:EndpointID>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="ZZZ">345KS5324</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Buyercompany ltd</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">1238764941386</cbc:ID>
|
||||
<cbc:Postbox>123</cbc:Postbox>
|
||||
<cbc:StreetName>Anystreet</cbc:StreetName>
|
||||
<cbc:AdditionalStreetName>Back door</cbc:AdditionalStreetName>
|
||||
<cbc:BuildingNumber>8</cbc:BuildingNumber>
|
||||
<cbc:Department>Accounting department</cbc:Department>
|
||||
<cbc:CityName>Anytown</cbc:CityName>
|
||||
<cbc:PostalZone>101</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RegionB</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode listID="ISO3166-1" listAgencyID="6">BE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID schemeID="BEVAT" schemeAgencyID="ZZZ">BE54321</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>The buyercompany inc.</cbc:RegistrationName>
|
||||
<cbc:CompanyID schemeAgencyID="ZZZ" schemeID="ZZZ">5645342123</cbc:CompanyID>
|
||||
<cac:RegistrationAddress>
|
||||
<cbc:CityName>Mainplace</cbc:CityName>
|
||||
<cbc:CountrySubentity>RegionB</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:RegistrationAddress>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Telephone>5121230</cbc:Telephone>
|
||||
<cbc:Telefax>5121231</cbc:Telefax>
|
||||
<cbc:ElectronicMail>john@buyercompany.eu</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
<cac:Person>
|
||||
<cbc:FirstName>John</cbc:FirstName>
|
||||
<cbc:FamilyName>X</cbc:FamilyName>
|
||||
<cbc:MiddleName>Doe</cbc:MiddleName>
|
||||
<cbc:JobTitle>Purchasing manager</cbc:JobTitle>
|
||||
</cac:Person>
|
||||
</cac:Party>
|
||||
</cac:AccountingCustomerParty>
|
||||
<cac:PayeeParty>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">098740918237</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Ebeneser Scrooge Inc.</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:CompanyID schemeID="UK:CH" schemeAgencyID="ZZZ">6411982340</cbc:CompanyID>
|
||||
</cac:PartyLegalEntity>
|
||||
</cac:PayeeParty>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Packing cost</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">100</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Promotion discount</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">100</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">292.20</cbc:TaxAmount>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">1460.5</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">292.1</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">1</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">0.1</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">-25</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">0</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">E</cbc:ID>
|
||||
<cbc:Percent>0</cbc:Percent>
|
||||
<cbc:TaxExemptionReasonCode listID="CWA 15577" listAgencyID="ZZZ">AAM</cbc:TaxExemptionReasonCode>
|
||||
<cbc:TaxExemptionReason>Exempt New Means of Transport</cbc:TaxExemptionReason>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
</cac:TaxTotal>
|
||||
<cac:LegalMonetaryTotal>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">1436.5</cbc:LineExtensionAmount>
|
||||
<cbc:TaxExclusiveAmount currencyID="EUR">1436.5</cbc:TaxExclusiveAmount>
|
||||
<cbc:TaxInclusiveAmount currencyID="EUR">1729</cbc:TaxInclusiveAmount>
|
||||
<cbc:AllowanceTotalAmount currencyID="EUR">100</cbc:AllowanceTotalAmount>
|
||||
<cbc:ChargeTotalAmount currencyID="EUR">100</cbc:ChargeTotalAmount>
|
||||
<cbc:PrepaidAmount currencyID="EUR">1000</cbc:PrepaidAmount>
|
||||
<cbc:PayableRoundingAmount currencyID="EUR">0.30</cbc:PayableRoundingAmount>
|
||||
<cbc:PayableAmount currencyID="EUR">729</cbc:PayableAmount>
|
||||
</cac:LegalMonetaryTotal>
|
||||
<cac:CreditNoteLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
<cbc:Note>Scratch on box</cbc:Note>
|
||||
<cbc:CreditedQuantity unitCode="C62">1</cbc:CreditedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">1273</cbc:LineExtensionAmount>
|
||||
<cbc:AccountingCost>BookingCode001</cbc:AccountingCost>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">254.6</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Description languageID="EN">Processor: Intel Core 2 Duo SU9400 LV (1.4GHz). RAM:
|
||||
3MB. Screen 1440x900</cbc:Description>
|
||||
<cbc:Name>Labtop computer</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB007</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890124</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344321</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434568</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
<cac:AdditionalItemProperty>
|
||||
<cbc:Name>Color</cbc:Name>
|
||||
<cbc:Value>black</cbc:Value>
|
||||
</cac:AdditionalItemProperty>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">1273</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Contract</cbc:AllowanceChargeReason>
|
||||
<cbc:MultiplierFactorNumeric>0.15</cbc:MultiplierFactorNumeric>
|
||||
<cbc:Amount currencyID="EUR">225</cbc:Amount>
|
||||
<cbc:BaseAmount currencyID="EUR">1500</cbc:BaseAmount>
|
||||
</cac:AllowanceCharge>
|
||||
</cac:Price>
|
||||
</cac:CreditNoteLine>
|
||||
<cac:CreditNoteLine>
|
||||
<cbc:ID>2</cbc:ID>
|
||||
<cbc:Note>Cover is slightly damaged.</cbc:Note>
|
||||
<cbc:CreditedQuantity unitCode="C62">-1</cbc:CreditedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">-3.96</cbc:LineExtensionAmount>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">-0.396</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Returned "Advanced computing" book</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB008</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890125</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">32344324</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434567</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">3.96</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:CreditNoteLine>
|
||||
<cac:CreditNoteLine>
|
||||
<cbc:ID>3</cbc:ID>
|
||||
<cbc:CreditedQuantity unitCode="C62">2</cbc:CreditedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">4.96</cbc:LineExtensionAmount>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">0.496</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>"Computing for dummies" book</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB009</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890126</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">32344324</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434566</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">2.48</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Contract</cbc:AllowanceChargeReason>
|
||||
<cbc:MultiplierFactorNumeric>0.1</cbc:MultiplierFactorNumeric>
|
||||
<cbc:Amount currencyID="EUR">0.275</cbc:Amount>
|
||||
<cbc:BaseAmount currencyID="EUR">2.75</cbc:BaseAmount>
|
||||
</cac:AllowanceCharge>
|
||||
</cac:Price>
|
||||
</cac:CreditNoteLine>
|
||||
<cac:CreditNoteLine>
|
||||
<cbc:ID>4</cbc:ID>
|
||||
<cbc:CreditedQuantity unitCode="C62">-1</cbc:CreditedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">-25</cbc:LineExtensionAmount>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">0</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Returned IBM 5150 desktop</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB010</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890127</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344322</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434565</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">E</cbc:ID>
|
||||
<cbc:Percent>0</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">25</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:CreditNoteLine>
|
||||
<cac:CreditNoteLine>
|
||||
<cbc:ID>5</cbc:ID>
|
||||
<cbc:CreditedQuantity unitCode="C62">250</cbc:CreditedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">187.5</cbc:LineExtensionAmount>
|
||||
<cbc:AccountingCost>BookingCode002</cbc:AccountingCost>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">37.5</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Network cable</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB011</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890128</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344325</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434564</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
<cac:AdditionalItemProperty>
|
||||
<cbc:Name>Type</cbc:Name>
|
||||
<cbc:Value>Cat5</cbc:Value>
|
||||
</cac:AdditionalItemProperty>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">0.75</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:CreditNoteLine>
|
||||
</CreditNote>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
||||
|
||||
<cbc:ID>123</cbc:ID>
|
||||
<cbc:IssueDate>2011-09-22</cbc:IssueDate>
|
||||
|
||||
<cac:InvoicePeriod>
|
||||
<cbc:StartDate>2011-08-01</cbc:StartDate>
|
||||
<cbc:EndDate>2011-08-31</cbc:EndDate>
|
||||
</cac:InvoicePeriod>
|
||||
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Custom Cotter Pins</cbc:Name>
|
||||
</cac:PartyName>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:AccountingCustomerParty>
|
||||
<cac:Party>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>North American Veeblefetzer</cbc:Name>
|
||||
</cac:PartyName>
|
||||
</cac:Party>
|
||||
</cac:AccountingCustomerParty>
|
||||
|
||||
<cac:LegalMonetaryTotal>
|
||||
<cbc:PayableAmount currencyID="CAD">100.00</cbc:PayableAmount>
|
||||
</cac:LegalMonetaryTotal>
|
||||
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
<cbc:LineExtensionAmount currencyID="CAD">100.00</cbc:LineExtensionAmount>
|
||||
<cac:Item>
|
||||
<cbc:Description>Cotter pin, MIL-SPEC</cbc:Description>
|
||||
</cac:Item>
|
||||
</cac:InvoiceLine>
|
||||
|
||||
</Invoice>
|
||||
@@ -0,0 +1,470 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2">
|
||||
<cbc:UBLVersionID>2.1</cbc:UBLVersionID>
|
||||
<cbc:ID>TOSL108</cbc:ID>
|
||||
<cbc:IssueDate>2009-12-15</cbc:IssueDate>
|
||||
<cbc:InvoiceTypeCode listID="UN/ECE 1001 Subset" listAgencyID="6">380</cbc:InvoiceTypeCode>
|
||||
<cbc:Note languageID="en">Ordered in our booth at the convention.</cbc:Note>
|
||||
<cbc:TaxPointDate>2009-11-30</cbc:TaxPointDate>
|
||||
<cbc:DocumentCurrencyCode listID="ISO 4217 Alpha" listAgencyID="6">EUR</cbc:DocumentCurrencyCode>
|
||||
<cbc:AccountingCost>Project cost code 123</cbc:AccountingCost>
|
||||
<cac:InvoicePeriod>
|
||||
<cbc:StartDate>2009-11-01</cbc:StartDate>
|
||||
<cbc:EndDate>2009-11-30</cbc:EndDate>
|
||||
</cac:InvoicePeriod>
|
||||
<cac:OrderReference>
|
||||
<cbc:ID>123</cbc:ID>
|
||||
</cac:OrderReference>
|
||||
<cac:ContractDocumentReference>
|
||||
<cbc:ID>Contract321</cbc:ID>
|
||||
<cbc:DocumentType>Framework agreement</cbc:DocumentType>
|
||||
</cac:ContractDocumentReference>
|
||||
<cac:AdditionalDocumentReference>
|
||||
<cbc:ID>Doc1</cbc:ID>
|
||||
<cbc:DocumentType>Timesheet</cbc:DocumentType>
|
||||
<cac:Attachment>
|
||||
<cac:ExternalReference>
|
||||
<cbc:URI>http://www.suppliersite.eu/sheet001.html</cbc:URI>
|
||||
</cac:ExternalReference>
|
||||
</cac:Attachment>
|
||||
</cac:AdditionalDocumentReference>
|
||||
<cac:AdditionalDocumentReference>
|
||||
<cbc:ID>Doc2</cbc:ID>
|
||||
<cbc:DocumentType>Drawing</cbc:DocumentType>
|
||||
<cac:Attachment>
|
||||
<cbc:EmbeddedDocumentBinaryObject mimeCode="application/pdf">UjBsR09EbGhjZ0dTQUxNQUFBUUNBRU1tQ1p0dU1GUXhEUzhi</cbc:EmbeddedDocumentBinaryObject>
|
||||
</cac:Attachment>
|
||||
</cac:AdditionalDocumentReference>
|
||||
<cac:AccountingSupplierParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="GLN" schemeAgencyID="9">1234567890123</cbc:EndpointID>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="ZZZ">Supp123</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Salescompany ltd.</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">1231412341324</cbc:ID>
|
||||
<cbc:Postbox>5467</cbc:Postbox>
|
||||
<cbc:StreetName>Main street</cbc:StreetName>
|
||||
<cbc:AdditionalStreetName>Suite 123</cbc:AdditionalStreetName>
|
||||
<cbc:BuildingNumber>1</cbc:BuildingNumber>
|
||||
<cbc:Department>Revenue department</cbc:Department>
|
||||
<cbc:CityName>Big city</cbc:CityName>
|
||||
<cbc:PostalZone>54321</cbc:PostalZone>
|
||||
<cbc:CountrySubentityCode>RegionA</cbc:CountrySubentityCode>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode listID="ISO3166-1" listAgencyID="6">DK</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID schemeID="DKVAT" schemeAgencyID="ZZZ">DK12345</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>The Sellercompany Incorporated</cbc:RegistrationName>
|
||||
<cbc:CompanyID schemeID="CVR" schemeAgencyID="ZZZ">5402697509</cbc:CompanyID>
|
||||
<cac:RegistrationAddress>
|
||||
<cbc:CityName>Big city</cbc:CityName>
|
||||
<cbc:CountrySubentity>RegionA</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>DK</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:RegistrationAddress>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Telephone>4621230</cbc:Telephone>
|
||||
<cbc:Telefax>4621231</cbc:Telefax>
|
||||
<cbc:ElectronicMail>antonio@salescompany.dk</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
<cac:Person>
|
||||
<cbc:FirstName>Antonio</cbc:FirstName>
|
||||
<cbc:FamilyName>Salemacher</cbc:FamilyName>
|
||||
<cbc:MiddleName>M</cbc:MiddleName>
|
||||
<cbc:JobTitle>Sales manager</cbc:JobTitle>
|
||||
</cac:Person>
|
||||
</cac:Party>
|
||||
</cac:AccountingSupplierParty>
|
||||
<cac:AccountingCustomerParty>
|
||||
<cac:Party>
|
||||
<cbc:EndpointID schemeID="GLN" schemeAgencyID="9">1234567987654</cbc:EndpointID>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="ZZZ">345KS5324</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Buyercompany ltd</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PostalAddress>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">1238764941386</cbc:ID>
|
||||
<cbc:Postbox>123</cbc:Postbox>
|
||||
<cbc:StreetName>Anystreet</cbc:StreetName>
|
||||
<cbc:AdditionalStreetName>Back door</cbc:AdditionalStreetName>
|
||||
<cbc:BuildingNumber>8</cbc:BuildingNumber>
|
||||
<cbc:Department>Accounting department</cbc:Department>
|
||||
<cbc:CityName>Anytown</cbc:CityName>
|
||||
<cbc:PostalZone>101</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RegionB</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode listID="ISO3166-1" listAgencyID="6">BE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:PostalAddress>
|
||||
<cac:PartyTaxScheme>
|
||||
<cbc:CompanyID schemeID="BEVAT" schemeAgencyID="ZZZ">BE54321</cbc:CompanyID>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:PartyTaxScheme>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:RegistrationName>The buyercompany inc.</cbc:RegistrationName>
|
||||
<cbc:CompanyID schemeAgencyID="ZZZ" schemeID="ZZZ">5645342123</cbc:CompanyID>
|
||||
<cac:RegistrationAddress>
|
||||
<cbc:CityName>Mainplace</cbc:CityName>
|
||||
<cbc:CountrySubentity>RegionB</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:RegistrationAddress>
|
||||
</cac:PartyLegalEntity>
|
||||
<cac:Contact>
|
||||
<cbc:Telephone>5121230</cbc:Telephone>
|
||||
<cbc:Telefax>5121231</cbc:Telefax>
|
||||
<cbc:ElectronicMail>john@buyercompany.eu</cbc:ElectronicMail>
|
||||
</cac:Contact>
|
||||
<cac:Person>
|
||||
<cbc:FirstName>John</cbc:FirstName>
|
||||
<cbc:FamilyName>Doe</cbc:FamilyName>
|
||||
<cbc:MiddleName>X</cbc:MiddleName>
|
||||
<cbc:JobTitle>Purchasing manager</cbc:JobTitle>
|
||||
</cac:Person>
|
||||
</cac:Party>
|
||||
</cac:AccountingCustomerParty>
|
||||
<cac:PayeeParty>
|
||||
<cac:PartyIdentification>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">098740918237</cbc:ID>
|
||||
</cac:PartyIdentification>
|
||||
<cac:PartyName>
|
||||
<cbc:Name>Ebeneser Scrooge Inc.</cbc:Name>
|
||||
</cac:PartyName>
|
||||
<cac:PartyLegalEntity>
|
||||
<cbc:CompanyID schemeID="UK:CH" schemeAgencyID="ZZZ">6411982340</cbc:CompanyID>
|
||||
</cac:PartyLegalEntity>
|
||||
</cac:PayeeParty>
|
||||
<cac:Delivery>
|
||||
<cbc:ActualDeliveryDate>2009-12-15</cbc:ActualDeliveryDate>
|
||||
<cac:DeliveryLocation>
|
||||
<cbc:ID schemeID="GLN" schemeAgencyID="9">6754238987648</cbc:ID>
|
||||
<cac:Address>
|
||||
<cbc:StreetName>Deliverystreet</cbc:StreetName>
|
||||
<cbc:AdditionalStreetName>Side door</cbc:AdditionalStreetName>
|
||||
<cbc:BuildingNumber>12</cbc:BuildingNumber>
|
||||
<cbc:CityName>DeliveryCity</cbc:CityName>
|
||||
<cbc:PostalZone>523427</cbc:PostalZone>
|
||||
<cbc:CountrySubentity>RegionC</cbc:CountrySubentity>
|
||||
<cac:Country>
|
||||
<cbc:IdentificationCode>BE</cbc:IdentificationCode>
|
||||
</cac:Country>
|
||||
</cac:Address>
|
||||
</cac:DeliveryLocation>
|
||||
</cac:Delivery>
|
||||
<cac:PaymentMeans>
|
||||
<cbc:PaymentMeansCode listID="UN/ECE 4461">31</cbc:PaymentMeansCode>
|
||||
<cbc:PaymentDueDate>2009-12-31</cbc:PaymentDueDate>
|
||||
<cbc:PaymentChannelCode>IBAN</cbc:PaymentChannelCode>
|
||||
<cbc:PaymentID>Payref1</cbc:PaymentID>
|
||||
<cac:PayeeFinancialAccount>
|
||||
<cbc:ID>DK1212341234123412</cbc:ID>
|
||||
<cac:FinancialInstitutionBranch>
|
||||
<cac:FinancialInstitution>
|
||||
<cbc:ID>DKDKABCD</cbc:ID>
|
||||
</cac:FinancialInstitution>
|
||||
</cac:FinancialInstitutionBranch>
|
||||
</cac:PayeeFinancialAccount>
|
||||
</cac:PaymentMeans>
|
||||
<cac:PaymentTerms>
|
||||
<cbc:Note>Penalty percentage 10% from due date</cbc:Note>
|
||||
</cac:PaymentTerms>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Packing cost</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">100</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Promotion discount</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">100</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">292.20</cbc:TaxAmount>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">1460.5</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">292.1</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">1</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">0.1</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
<cac:TaxSubtotal>
|
||||
<cbc:TaxableAmount currencyID="EUR">-25</cbc:TaxableAmount>
|
||||
<cbc:TaxAmount currencyID="EUR">0</cbc:TaxAmount>
|
||||
<cac:TaxCategory>
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">E</cbc:ID>
|
||||
<cbc:Percent>0</cbc:Percent>
|
||||
<cbc:TaxExemptionReasonCode listID="CWA 15577" listAgencyID="ZZZ">AAM</cbc:TaxExemptionReasonCode>
|
||||
<cbc:TaxExemptionReason>Exempt New Means of Transport</cbc:TaxExemptionReason>
|
||||
<cac:TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</cac:TaxScheme>
|
||||
</cac:TaxCategory>
|
||||
</cac:TaxSubtotal>
|
||||
</cac:TaxTotal>
|
||||
<cac:LegalMonetaryTotal>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">1436.5</cbc:LineExtensionAmount>
|
||||
<cbc:TaxExclusiveAmount currencyID="EUR">1436.5</cbc:TaxExclusiveAmount>
|
||||
<cbc:TaxInclusiveAmount currencyID="EUR">1729</cbc:TaxInclusiveAmount>
|
||||
<cbc:AllowanceTotalAmount currencyID="EUR">100</cbc:AllowanceTotalAmount>
|
||||
<cbc:ChargeTotalAmount currencyID="EUR">100</cbc:ChargeTotalAmount>
|
||||
<cbc:PrepaidAmount currencyID="EUR">1000</cbc:PrepaidAmount>
|
||||
<cbc:PayableRoundingAmount currencyID="EUR">0.30</cbc:PayableRoundingAmount>
|
||||
<cbc:PayableAmount currencyID="EUR">729</cbc:PayableAmount>
|
||||
</cac:LegalMonetaryTotal>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>1</cbc:ID>
|
||||
<cbc:Note>Scratch on box</cbc:Note>
|
||||
<cbc:InvoicedQuantity unitCode="C62">1</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">1273</cbc:LineExtensionAmount>
|
||||
<cbc:AccountingCost>BookingCode001</cbc:AccountingCost>
|
||||
<cac:OrderLineReference>
|
||||
<cbc:LineID>1</cbc:LineID>
|
||||
</cac:OrderLineReference>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Damage</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">12</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>true</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Testing</cbc:AllowanceChargeReason>
|
||||
<cbc:Amount currencyID="EUR">10</cbc:Amount>
|
||||
</cac:AllowanceCharge>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">254.6</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Description languageID="EN">Processor: Intel Core 2 Duo SU9400 LV (1.4GHz). RAM:
|
||||
3MB. Screen 1440x900</cbc:Description>
|
||||
<cbc:Name>Labtop computer</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB007</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890124</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344321</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434568</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
<cac:AdditionalItemProperty>
|
||||
<cbc:Name>Color</cbc:Name>
|
||||
<cbc:Value>black</cbc:Value>
|
||||
</cac:AdditionalItemProperty>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">1273</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Contract</cbc:AllowanceChargeReason>
|
||||
<cbc:MultiplierFactorNumeric>0.15</cbc:MultiplierFactorNumeric>
|
||||
<cbc:Amount currencyID="EUR">225</cbc:Amount>
|
||||
<cbc:BaseAmount currencyID="EUR">1500</cbc:BaseAmount>
|
||||
</cac:AllowanceCharge>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>2</cbc:ID>
|
||||
<cbc:Note>Cover is slightly damaged.</cbc:Note>
|
||||
<cbc:InvoicedQuantity unitCode="C62">-1</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">-3.96</cbc:LineExtensionAmount>
|
||||
<cac:OrderLineReference>
|
||||
<cbc:LineID>5</cbc:LineID>
|
||||
</cac:OrderLineReference>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">-0.396</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Returned "Advanced computing" book</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB008</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890125</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">32344324</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434567</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">3.96</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>3</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="C62">2</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">4.96</cbc:LineExtensionAmount>
|
||||
<cac:OrderLineReference>
|
||||
<cbc:LineID>3</cbc:LineID>
|
||||
</cac:OrderLineReference>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">0.496</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>"Computing for dummies" book</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB009</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890126</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">32344324</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434566</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">AA</cbc:ID>
|
||||
<cbc:Percent>10</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">2.48</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
<cac:AllowanceCharge>
|
||||
<cbc:ChargeIndicator>false</cbc:ChargeIndicator>
|
||||
<cbc:AllowanceChargeReason>Contract</cbc:AllowanceChargeReason>
|
||||
<cbc:MultiplierFactorNumeric>0.1</cbc:MultiplierFactorNumeric>
|
||||
<cbc:Amount currencyID="EUR">0.275</cbc:Amount>
|
||||
<cbc:BaseAmount currencyID="EUR">2.75</cbc:BaseAmount>
|
||||
</cac:AllowanceCharge>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>4</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="C62">-1</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">-25</cbc:LineExtensionAmount>
|
||||
<cac:OrderLineReference>
|
||||
<cbc:LineID>2</cbc:LineID>
|
||||
</cac:OrderLineReference>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">0</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Returned IBM 5150 desktop</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB010</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890127</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344322</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434565</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">E</cbc:ID>
|
||||
<cbc:Percent>0</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">25</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
<cac:InvoiceLine>
|
||||
<cbc:ID>5</cbc:ID>
|
||||
<cbc:InvoicedQuantity unitCode="C62">250</cbc:InvoicedQuantity>
|
||||
<cbc:LineExtensionAmount currencyID="EUR">187.5</cbc:LineExtensionAmount>
|
||||
<cbc:AccountingCost>BookingCode002</cbc:AccountingCost>
|
||||
<cac:OrderLineReference>
|
||||
<cbc:LineID>4</cbc:LineID>
|
||||
</cac:OrderLineReference>
|
||||
<cac:TaxTotal>
|
||||
<cbc:TaxAmount currencyID="EUR">37.5</cbc:TaxAmount>
|
||||
</cac:TaxTotal>
|
||||
<cac:Item>
|
||||
<cbc:Name>Network cable</cbc:Name>
|
||||
<cac:SellersItemIdentification>
|
||||
<cbc:ID>JB011</cbc:ID>
|
||||
</cac:SellersItemIdentification>
|
||||
<cac:StandardItemIdentification>
|
||||
<cbc:ID schemeID="GTIN" schemeAgencyID="9">1234567890128</cbc:ID>
|
||||
</cac:StandardItemIdentification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="113" listID="UNSPSC">12344325</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<cac:CommodityClassification>
|
||||
<cbc:ItemClassificationCode listAgencyID="2" listID="CPV">65434564</cbc:ItemClassificationCode>
|
||||
</cac:CommodityClassification>
|
||||
<ClassifiedTaxCategory xmlns="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2">
|
||||
<cbc:ID schemeID="UN/ECE 5305" schemeAgencyID="6">S</cbc:ID>
|
||||
<cbc:Percent>20</cbc:Percent>
|
||||
<TaxScheme>
|
||||
<cbc:ID schemeID="UN/ECE 5153" schemeAgencyID="6">VAT</cbc:ID>
|
||||
</TaxScheme>
|
||||
</ClassifiedTaxCategory>
|
||||
<cac:AdditionalItemProperty>
|
||||
<cbc:Name>Type</cbc:Name>
|
||||
<cbc:Value>Cat5</cbc:Value>
|
||||
</cac:AdditionalItemProperty>
|
||||
</cac:Item>
|
||||
<cac:Price>
|
||||
<cbc:PriceAmount currencyID="EUR">0.75</cbc:PriceAmount>
|
||||
<cbc:BaseQuantity unitCode="C62">1</cbc:BaseQuantity>
|
||||
</cac:Price>
|
||||
</cac:InvoiceLine>
|
||||
</Invoice>
|
||||
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{"cash_rounding": true}
|
||||
,{"cash_rounding": false}
|
||||
]
|
||||
@@ -0,0 +1,90 @@
|
||||
=======================
|
||||
UBL 2 Credit Note Parse
|
||||
=======================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_tax
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
>>> from trytond.tools import file_open
|
||||
|
||||
>>> cash_rounding = globals().get('cash_rounding', False)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> modules = ['edocument_ubl', 'account_invoice']
|
||||
>>> if cash_rounding:
|
||||
... modules.append('account_cash_rounding')
|
||||
>>> config = activate_modules(modules, create_company, create_chart)
|
||||
|
||||
>>> Attachment = Model.get('ir.attachment')
|
||||
>>> EInvoice = Model.get('edocument.ubl.invoice')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
|
||||
Setup company::
|
||||
|
||||
>>> company = get_company()
|
||||
>>> company.party.name = 'Buyercompany ltd'
|
||||
>>> company.party.save()
|
||||
|
||||
Create currency::
|
||||
|
||||
>>> eur = get_currency('EUR')
|
||||
>>> if cash_rounding:
|
||||
... eur.cash_rounding = Decimal('1')
|
||||
>>> eur.save()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax20 = create_tax(Decimal('.2'))
|
||||
>>> tax20.unece_category_code = 'S'
|
||||
>>> tax20.unece_code = 'VAT'
|
||||
>>> tax20.save()
|
||||
|
||||
>>> tax10 = create_tax(Decimal('.1'))
|
||||
>>> tax10.unece_category_code = 'AA'
|
||||
>>> tax10.unece_code = 'VAT'
|
||||
>>> tax10.save()
|
||||
|
||||
>>> tax0 = create_tax(Decimal('0'))
|
||||
>>> tax0.unece_category_code = 'E'
|
||||
>>> tax0.unece_code = 'VAT'
|
||||
>>> tax0.save()
|
||||
|
||||
Parse the UBL invoice::
|
||||
|
||||
>>> with file_open(
|
||||
... 'edocument_ubl/tests/UBL-CreditNote-2.1-Example.xml',
|
||||
... mode='rb') as fp:
|
||||
... invoice_id = EInvoice.parse(fp.read(), config.context)
|
||||
|
||||
>>> invoice = Invoice(invoice_id)
|
||||
|
||||
>>> invoice.reference
|
||||
'TOSL108'
|
||||
>>> assertEqual(invoice.invoice_date, dt.date(2009, 12, 15))
|
||||
>>> invoice.party.name
|
||||
'Salescompany ltd.'
|
||||
>>> invoice.invoice_address.rec_name
|
||||
'Salescompany ltd., Main street 1 5467, 54321, Big city'
|
||||
>>> assertEqual(invoice.company, company)
|
||||
>>> assertEqual(
|
||||
... invoice.total_amount,
|
||||
... Decimal('-1729.00') if cash_rounding else Decimal('-1728.70'))
|
||||
>>> invoice.tax_amount
|
||||
Decimal('-292.20')
|
||||
>>> len(invoice.lines)
|
||||
5
|
||||
|
||||
>>> attachments = Attachment.find([])
|
||||
>>> len(attachments)
|
||||
3
|
||||
>>> assertEqual({a.resource for a in attachments}, {invoice})
|
||||
>>> sorted((a.name, a.type) for a in attachments)
|
||||
[('Drawing Doc2.pdf', 'data'), ('Framework agreement Contract321', 'data'), ('Timesheet Doc1', 'link')]
|
||||
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{"cash_rounding": true}
|
||||
,{"cash_rounding": false}
|
||||
]
|
||||
@@ -0,0 +1,90 @@
|
||||
===================
|
||||
UBL 2 Invoice Parse
|
||||
===================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart, create_tax
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
>>> from trytond.tools import file_open
|
||||
|
||||
>>> cash_rounding = globals().get('cash_rounding', False)
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> modules = ['edocument_ubl', 'account_invoice']
|
||||
>>> if cash_rounding:
|
||||
... modules.append('account_cash_rounding')
|
||||
>>> config = activate_modules(modules, create_company, create_chart)
|
||||
|
||||
>>> Attachment = Model.get('ir.attachment')
|
||||
>>> EInvoice = Model.get('edocument.ubl.invoice')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
|
||||
Setup company::
|
||||
|
||||
>>> company = get_company()
|
||||
>>> company.party.name = 'Buyercompany ltd'
|
||||
>>> company.party.save()
|
||||
|
||||
Create currency::
|
||||
|
||||
>>> eur = get_currency('EUR')
|
||||
>>> if cash_rounding:
|
||||
... eur.cash_rounding = Decimal('1')
|
||||
>>> eur.save()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax20 = create_tax(Decimal('.2'))
|
||||
>>> tax20.unece_category_code = 'S'
|
||||
>>> tax20.unece_code = 'VAT'
|
||||
>>> tax20.save()
|
||||
|
||||
>>> tax10 = create_tax(Decimal('.1'))
|
||||
>>> tax10.unece_category_code = 'AA'
|
||||
>>> tax10.unece_code = 'VAT'
|
||||
>>> tax10.save()
|
||||
|
||||
>>> tax0 = create_tax(Decimal('0'))
|
||||
>>> tax0.unece_category_code = 'E'
|
||||
>>> tax0.unece_code = 'VAT'
|
||||
>>> tax0.save()
|
||||
|
||||
Parse the UBL invoice::
|
||||
|
||||
>>> with file_open(
|
||||
... 'edocument_ubl/tests/UBL-Invoice-2.1-Example.xml',
|
||||
... mode='rb') as fp:
|
||||
... invoice_id = EInvoice.parse(fp.read(), config.context)
|
||||
|
||||
>>> invoice = Invoice(invoice_id)
|
||||
|
||||
>>> invoice.reference
|
||||
'TOSL108'
|
||||
>>> assertEqual(invoice.invoice_date, dt.date(2009, 12, 15))
|
||||
>>> invoice.party.name
|
||||
'Salescompany ltd.'
|
||||
>>> invoice.invoice_address.rec_name
|
||||
'Salescompany ltd., Main street 1 5467, 54321, Big city'
|
||||
>>> assertEqual(invoice.company, company)
|
||||
>>> assertEqual(
|
||||
... invoice.total_amount,
|
||||
... Decimal('1729.00') if cash_rounding else Decimal('1728.70'))
|
||||
>>> invoice.tax_amount
|
||||
Decimal('292.20')
|
||||
>>> len(invoice.lines)
|
||||
5
|
||||
|
||||
>>> attachments = Attachment.find([])
|
||||
>>> len(attachments)
|
||||
3
|
||||
>>> assertEqual({a.resource for a in attachments}, {invoice})
|
||||
>>> sorted((a.name, a.type) for a in attachments)
|
||||
[('Drawing Doc2.pdf', 'data'), ('Framework agreement Contract321', 'data'), ('Timesheet Doc1', 'link')]
|
||||
@@ -0,0 +1,56 @@
|
||||
===========================
|
||||
UBL 2 Invoice Parse Trivial
|
||||
===========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import create_chart
|
||||
>>> from trytond.modules.company.tests.tools import create_company, get_company
|
||||
>>> from trytond.modules.currency.tests.tools import get_currency
|
||||
>>> from trytond.tests.tools import activate_modules, assertEqual
|
||||
>>> from trytond.tools import file_open
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules(
|
||||
... ['edocument_ubl', 'account_invoice'], create_company, create_chart)
|
||||
|
||||
>>> EInvoice = Model.get('edocument.ubl.invoice')
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
|
||||
Setup company::
|
||||
|
||||
>>> company = get_company()
|
||||
>>> company.party.name = 'North American Veeblefetzer'
|
||||
>>> company.party.save()
|
||||
|
||||
Create currency::
|
||||
|
||||
>>> eur = get_currency('CAD')
|
||||
|
||||
Parse the UBL invoice::
|
||||
|
||||
>>> with file_open(
|
||||
... 'edocument_ubl/tests/UBL-Invoice-2.1-Example-Trivial.xml',
|
||||
... mode='rb') as fp:
|
||||
... invoice_id = EInvoice.parse(fp.read(), config.context)
|
||||
|
||||
>>> invoice = Invoice(invoice_id)
|
||||
|
||||
>>> assertEqual(invoice.invoice_date, dt.date(2011, 9, 22))
|
||||
>>> invoice.party.name
|
||||
'Custom Cotter Pins'
|
||||
>>> assertEqual(invoice.company, company)
|
||||
>>> invoice.total_amount
|
||||
Decimal('100.00')
|
||||
>>> line, = invoice.lines
|
||||
>>> line.description
|
||||
'Cotter pin, MIL-SPEC'
|
||||
>>> line.quantity
|
||||
1.0
|
||||
>>> line.unit
|
||||
>>> line.unit_price
|
||||
Decimal('100.0000')
|
||||
@@ -9,8 +9,7 @@ from unittest.mock import MagicMock, Mock, patch
|
||||
from lxml import etree
|
||||
|
||||
from trytond.pool import Pool
|
||||
from trytond.tests.test_tryton import (
|
||||
ModuleTestCase, activate_module, with_transaction)
|
||||
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
|
||||
|
||||
|
||||
def get_invoice():
|
||||
@@ -135,11 +134,7 @@ def get_invoice():
|
||||
class EdocumentUblTestCase(ModuleTestCase):
|
||||
"Test Edocument Ubl module"
|
||||
module = 'edocument_ubl'
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
activate_module('account_invoice')
|
||||
extras = ['account_invoice', 'purchase']
|
||||
|
||||
@with_transaction()
|
||||
def test_Invoice_2(self):
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
|
||||
from trytond.tests.test_tryton import load_doc_tests
|
||||
|
||||
|
||||
def load_tests(*args, **kwargs):
|
||||
return load_doc_tests(__name__, __file__, *args, **kwargs)
|
||||
@@ -5,9 +5,12 @@ depends:
|
||||
ir
|
||||
party
|
||||
extras_depend:
|
||||
account_cash_rounding
|
||||
account_invoice
|
||||
purchase
|
||||
xml:
|
||||
account.xml
|
||||
message.xml
|
||||
|
||||
[register]
|
||||
model:
|
||||
@@ -18,3 +21,7 @@ model:
|
||||
model:
|
||||
edocument.Invoice
|
||||
account.InvoiceEdocumentStart
|
||||
|
||||
[register account_invoice purchase]
|
||||
model:
|
||||
edocument.Invoice_Purchase
|
||||
|
||||
Reference in New Issue
Block a user