[IMP+FIX] intrastat_product: Several things:

* Add product_origin_country_id on declaration/computation lines

  Copy incoterms and destination country from SO to invoice when invoicing from SO
  We need weight even when supplementary units is used
  Small cleanups and enhancements

* Add support for accessory costs

  Add default values for intrastat transaction on company
  Code cleanup

* If rounded weight is 0, put 1

  Take into account the taxes for B2C
  Small code cleanup

* Remove field exclude_from_intrastat

  Re-organise view of intrastat.product.declaration

* Add option intrastat_accessory_costs on company

  Set more fields as invisible (localisation should put them visible if they need it)
  Fix handling of suppl. units when hs_code is empty on invoice line (but set on product)
  Small usability enhancements

* Include selection of type of invoice in search, for better perf

  Isolate domain in a _prepare method, for easier inheritance
  WARNING: I changed the default selection of invoice type ; adapt it in your localization m
  odule if necessary
  Add intrastat_transaction_in_refund for company (not needed for France, but may be needed elsewhere)
  Add a log when an invoice line is skipped

* Include product code in warning msg on weight

* Inspired by the PR https://github.com/akretion/account-financial-reporting/pull/8 of Luc de Meyer
This commit is contained in:
Alexis de Lattre
2015-11-10 21:36:50 +01:00
committed by João Marques
parent 63ce502f88
commit 6cfc6f6d8a
18 changed files with 437 additions and 164 deletions

View File

@@ -1,3 +1,3 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
from . import models from . import models

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo
@@ -32,7 +32,8 @@
'depends': [ 'depends': [
'intrastat_base', 'intrastat_base',
'product_harmonized_system', 'product_harmonized_system',
'stock', 'sale_stock',
'purchase',
], ],
'conflicts': ['report_intrastat'], 'conflicts': ['report_intrastat'],
'data': [ 'data': [

View File

@@ -17,5 +17,12 @@
<field name="intrastat_unit_id" ref="intrastat_unit_pce"/> <field name="intrastat_unit_id" ref="intrastat_unit_pce"/>
</record> </record>
<record id="base.main_company" model="res.company">
<field name="intrastat_arrivals">extended</field>
<field name="intrastat_dispatches">extended</field>
<field name="intrastat_incoterm_id" ref="stock.incoterm_DDU"/>
<field name="intrastat_transport_id" ref="intrastat_transport_3"/>
</record>
</data> </data>
</openerp> </openerp>

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
from . import account_invoice from . import account_invoice
from . import hs_code from . import hs_code
from . import intrastat_product_declaration from . import intrastat_product_declaration
@@ -7,5 +7,6 @@ from . import intrastat_transaction
from . import intrastat_transport_mode from . import intrastat_transport_mode
from . import intrastat_unit from . import intrastat_unit
from . import res_company from . import res_company
from . import sale_order
from . import stock_picking from . import stock_picking
from . import stock_warehouse from . import stock_warehouse

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo
@@ -23,7 +23,8 @@
############################################################################## ##############################################################################
from openerp import models, fields, api, _ from openerp import models, fields, api, _
from openerp.exceptions import RedirectWarning, ValidationError, Warning from openerp.exceptions import RedirectWarning, ValidationError
from openerp.exceptions import Warning as UserError
import openerp.addons.decimal_precision as dp import openerp.addons.decimal_precision as dp
from datetime import datetime, date from datetime import datetime, date
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@@ -129,13 +130,13 @@ class IntrastatProductDeclaration(models.Model):
], string='Month', required=True, ], string='Month', required=True,
default=_get_month) default=_get_month)
year_month = fields.Char( year_month = fields.Char(
compute='_compute_year_month', string='Month', readonly=True, compute='_compute_year_month', string='Period', readonly=True,
track_visibility='always', store=True, track_visibility='onchange', store=True,
help="Year and month of the declaration.") help="Year and month of the declaration.")
type = fields.Selection( type = fields.Selection(
'_get_type', string='Type', required=True, '_get_type', string='Type', required=True,
states={'done': [('readonly', True)]}, states={'done': [('readonly', True)]},
track_visibility='always', help="Select the declaration type.") track_visibility='onchange', help="Select the declaration type.")
action = fields.Selection( action = fields.Selection(
'_get_action', '_get_action',
string='Action', required=True, string='Action', required=True,
@@ -159,8 +160,8 @@ class IntrastatProductDeclaration(models.Model):
store=True, track_visibility='onchange') store=True, track_visibility='onchange')
total_amount = fields.Float( total_amount = fields.Float(
compute='_compute_numbers', digits=dp.get_precision('Account'), compute='_compute_numbers', digits=dp.get_precision('Account'),
string='Total Amount', store=True, string='Total Fiscal Amount', store=True,
help="Total amount in company currency of the declaration.") help="Total fiscal amount in company currency of the declaration.")
currency_id = fields.Many2one( currency_id = fields.Many2one(
'res.currency', related='company_id.currency_id', readonly=True, 'res.currency', related='company_id.currency_id', readonly=True,
string='Currency') string='Currency')
@@ -240,13 +241,23 @@ class IntrastatProductDeclaration(models.Model):
return country return country
def _get_intrastat_transaction(self, inv_line): def _get_intrastat_transaction(self, inv_line):
return inv_line.invoice_id.intrastat_transaction_id invoice = inv_line.invoice_id
if invoice.intrastat_transaction_id:
return invoice.intrastat_transaction_id
else:
company = invoice.company_id
if invoice.type == 'out_invoice':
return company.intrastat_transaction_out_invoice
elif invoice.type == 'out_refund':
return company.intrastat_transaction_out_refund
elif invoice.type == 'in_invoice':
return company.intrastat_transaction_in_invoice
def _get_weight_and_supplunits(self, inv_line): def _get_weight_and_supplunits(self, inv_line, hs_code):
line_qty = inv_line.quantity line_qty = inv_line.quantity
product = inv_line.product_id product = inv_line.product_id
invoice = inv_line.invoice_id invoice = inv_line.invoice_id
intrastat_unit_id = inv_line.hs_code_id.intrastat_unit_id intrastat_unit_id = hs_code.intrastat_unit_id
source_uom = inv_line.uos_id source_uom = inv_line.uos_id
weight_uom_categ = self._uom_refs['weight_uom_categ'] weight_uom_categ = self._uom_refs['weight_uom_categ']
kg_uom = self._uom_refs['kg_uom'] kg_uom = self._uom_refs['kg_uom']
@@ -273,7 +284,7 @@ class IntrastatProductDeclaration(models.Model):
) % intrastat_unit_id.name ) % intrastat_unit_id.name
note += "\n" + _( note += "\n" + _(
"Please correct the Intrastat Supplementary Unit " "Please correct the Intrastat Supplementary Unit "
"settingsand regenerate the lines or adjust the lines " "settings and regenerate the lines or adjust the lines "
"with Intrastat Code '%s' manually" "with Intrastat Code '%s' manually"
) % inv_line.hs_code_id.local_code ) % inv_line.hs_code_id.local_code
self._note += note self._note += note
@@ -293,58 +304,54 @@ class IntrastatProductDeclaration(models.Model):
self._note += note self._note += note
return weight, suppl_unit_qty return weight, suppl_unit_qty
else: if source_uom == kg_uom:
if source_uom == kg_uom: weight = line_qty
weight = line_qty elif source_uom.category_id == weight_uom_categ:
elif source_uom.category_id == weight_uom_categ: weight = self.env['product.uom']._compute_qty_obj(
weight = self.env['product.uom']._compute_qty_obj( source_uom, line_qty, kg_uom)
source_uom, line_qty, kg_uom) elif source_uom.category_id == pce_uom_categ:
elif source_uom.category_id == pce_uom_categ: if not product.weight_net:
if not product.weight_net:
note = "\n" + _(
"Missing net weight on product '%s'."
) % product.name
note += "\n" + _(
"Please correct the product record and regenerate "
"the lines or adjust the impacted lines manually")
self._note += note
return weight, suppl_unit_qty
if source_uom == pce_uom:
weight = product.weight_net * line_qty
else:
# Here, I suppose that, on the product, the
# weight is per PCE and not per uom_id
weight = product.weight_net * \
self.env['product.uom']._compute_qty_obj(
source_uom, line_qty, pce_uom)
else:
note = "\n" + _( note = "\n" + _(
"Conversion from unit of measure '%s' to 'Kg' " "Missing net weight on product %s."
"is not implemented yet." ) % product.name_get()[0][1]
) % source_uom.name
note += "\n" + _( note += "\n" + _(
"Please correct the unit of measure settings and " "Please correct the product record and regenerate "
"regenerate the lines or adjust the impacted lines " "the lines or adjust the impacted lines manually")
"manually")
self._note += note self._note += note
return weight, suppl_unit_qty return weight, suppl_unit_qty
if source_uom == pce_uom:
weight = product.weight_net * line_qty
else:
# Here, I suppose that, on the product, the
# weight is per PCE and not per uom_id
weight = product.weight_net * \
self.env['product.uom']._compute_qty_obj(
source_uom, line_qty, pce_uom)
else:
note = "\n" + _(
"Conversion from unit of measure '%s' to 'Kg' "
"is not implemented yet."
) % source_uom.name
note += "\n" + _(
"Please correct the unit of measure settings and "
"regenerate the lines or adjust the impacted lines "
"manually")
self._note += note
return weight, suppl_unit_qty
return weight, suppl_unit_qty return weight, suppl_unit_qty
def _get_amount(self, inv_line): def _get_amount(self, inv_line):
invoice = inv_line.invoice_id invoice = inv_line.invoice_id
amount = inv_line.price_subtotal amount = invoice.currency_id.with_context(
if invoice.currency_id.name != 'EUR': date=invoice.date_invoice).compute(
amount = self.env['res.currency'].with_context( inv_line.price_subtotal,
date=invoice.date_invoice).compute( self.company_id.currency_id)
invoice.currency_id,
self.company_id.currency_id,
amount)
return amount return amount
def _get_region(self, inv_line): def _get_region(self, inv_line):
""" """
Logic copied from standard addons, l10n_be_intrastat module: Logic copied from standard addons
If purchase, comes from purchase order, linked to a location, If purchase, comes from purchase order, linked to a location,
which is linked to the warehouse. which is linked to the warehouse.
@@ -361,8 +368,7 @@ class IntrastatProductDeclaration(models.Model):
[('invoice_lines', 'in', inv_line.id)]) [('invoice_lines', 'in', inv_line.id)])
if po_lines: if po_lines:
po = po_lines.order_id po = po_lines.order_id
region = self.env['stock.warehouse'].get_region_from_location( region = po.location_id.get_intrastat_region()
po.location_id)
elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'): elif inv_line.invoice_id.type in ('out_invoice', 'out_refund'):
so_lines = self.env['sale.order.line'].search( so_lines = self.env['sale.order.line'].search(
[('invoice_lines', 'in', inv_line.id)]) [('invoice_lines', 'in', inv_line.id)])
@@ -374,7 +380,6 @@ class IntrastatProductDeclaration(models.Model):
region = self.company_id.intrastat_region_id region = self.company_id.intrastat_region_id
return region return region
def _get_transport(self, inv_line): def _get_transport(self, inv_line):
transport = inv_line.invoice_id.intrastat_transport_id \ transport = inv_line.invoice_id.intrastat_transport_id \
or self.company_id.intrastat_transport_id or self.company_id.intrastat_transport_id
@@ -397,80 +402,164 @@ class IntrastatProductDeclaration(models.Model):
self._company_warning(msg) self._company_warning(msg)
return incoterm return incoterm
def _get_product_origin_country(self, inv_line):
return inv_line.product_id.origin_country_id
def _update_computation_line_vals(self, inv_line, line_vals): def _update_computation_line_vals(self, inv_line, line_vals):
""" placeholder for localization modules """ """ placeholder for localization modules """
pass pass
def _gather_invoices(self): def _prepare_invoice_domain(self):
lines = []
start_date = date(self.year, self.month, 1) start_date = date(self.year, self.month, 1)
end_date = start_date + relativedelta(day=1, months=+1, days=-1) end_date = start_date + relativedelta(day=1, months=+1, days=-1)
domain = [
invoices = self.env['account.invoice'].search([
('date_invoice', '>=', start_date), ('date_invoice', '>=', start_date),
('date_invoice', '<=', end_date), ('date_invoice', '<=', end_date),
('state', 'in', ['open', 'paid']), ('state', 'in', ['open', 'paid']),
('intrastat_country', '=', True), ('intrastat_country', '=', True),
('company_id', '=', self.company_id.id)]) ('company_id', '=', self.company_id.id)]
if self.type == 'arrivals':
domain.append(('type', 'in', ('in_invoice', 'in_refund')))
elif self.type == 'dispatches':
domain.append(('type', 'in', ('out_invoice', 'out_refund')))
return domain
def _gather_invoices(self):
lines = []
accessory_costs = self.company_id.intrastat_accessory_costs
domain = self._prepare_invoice_domain()
invoices = self.env['account.invoice'].search(domain)
for invoice in invoices: for invoice in invoices:
if self.type == 'arrivals': lines_current_invoice = []
if invoice.type in ['out_invoice', 'in_refund']: total_inv_accessory_costs_cc = 0.0 # in company currency
continue total_inv_product_cc = 0.0 # in company currency
else:
if invoice.type in ['in_invoice', 'out_refund']:
continue
for inv_line in invoice.invoice_line: for inv_line in invoice.invoice_line:
intrastat = inv_line.hs_code_id if (
if not intrastat: accessory_costs and
inv_line.product_id and
inv_line.product_id.is_accessory_cost):
acost = invoice.currency_id.with_context(
date=invoice.date_invoice).compute(
inv_line.price_subtotal,
self.company_id.currency_id)
total_inv_accessory_costs_cc += acost
continue continue
if not inv_line.quantity: if not inv_line.quantity:
_logger.info(
'Skipping invoice line %s qty %s '
'of invoice %s. Reason: qty = 0'
% (inv_line.name, inv_line.quantity, invoice.number))
continue
if not inv_line.price_subtotal:
_logger.info(
'Skipping invoice line %s qty %s '
'of invoice %s. Reason: price_subtotal = 0'
% (inv_line.name, inv_line.quantity, invoice.number))
continue continue
partner_country = self._get_partner_country(inv_line) partner_country = self._get_partner_country(inv_line)
if not partner_country: if not partner_country:
_logger.info(
'Skipping invoice line %s qty %s'
'of invoice %s. Reason: not partner_country'
% (inv_line.name, inv_line.quantity, invoice.number))
continue
if any([
tax.exclude_from_intrastat_if_present
for tax in inv_line.invoice_line_tax_id]):
_logger.info(
'Skipping invoice line %s '
'qty %s of invoice %s. Reason: '
'tax.exclude_from_intrastat_if_present'
% (inv_line.name, inv_line.quantity, invoice.number))
continue
if inv_line.hs_code_id:
hs_code = inv_line.hs_code_id
elif (
inv_line.product_id and
inv_line.product_id.type in ('product', 'consu')):
hs_code = inv_line.product_id.product_tmpl_id.\
get_hs_code_recursively()
if not hs_code:
note = "\n" + _(
"Missing H.S. code on product %s. "
"This product is present in invoice %s.") % (
inv_line.product_id.name_get()[0][1],
inv_line.invoice_id.number)
self._note += note
continue
else:
_logger.info(
'Skipping invoice line %s qty %s'
'of invoice %s. Reason: no product nor hs_code'
% (inv_line.name, inv_line.quantity, invoice.number))
continue continue
intrastat_transaction = \ intrastat_transaction = \
self._get_intrastat_transaction(inv_line) self._get_intrastat_transaction(inv_line)
weight, suppl_unit_qty = self._get_weight_and_supplunits( weight, suppl_unit_qty = self._get_weight_and_supplunits(
inv_line) inv_line, hs_code)
amount_company_currency = self._get_amount(inv_line) amount_company_currency = self._get_amount(inv_line)
total_inv_product_cc += amount_company_currency
product_origin_country = self._get_product_origin_country(
inv_line)
line_vals = { line_vals = {
'parent_id': self.id, 'parent_id': self.id,
'invoice_line_id': inv_line.id, 'invoice_line_id': inv_line.id,
'src_dest_country_id': partner_country.id, 'src_dest_country_id': partner_country.id,
'product_id': inv_line.product_id.id, 'product_id': inv_line.product_id.id,
'hs_code_id': intrastat.id, 'hs_code_id': hs_code.id,
'weight': weight, 'weight': weight,
'suppl_unit_qty': suppl_unit_qty, 'suppl_unit_qty': suppl_unit_qty,
'amount_company_currency': amount_company_currency, 'amount_company_currency': amount_company_currency,
'amount_accessory_cost_company_currency': 0.0,
'transaction_id': intrastat_transaction.id, 'transaction_id': intrastat_transaction.id,
'product_origin_country_id':
product_origin_country.id or False,
} }
# extended declaration # extended declaration
if self._extended: if self._extended:
transport = self._get_transport(inv_line) transport = self._get_transport(inv_line)
region = self._get_region(inv_line)
line_vals.update({ line_vals.update({
'transport_id': transport.id, 'transport_id': transport.id,
'region_id': region and region.id or False,
}) })
self._update_computation_line_vals(inv_line, line_vals) self._update_computation_line_vals(inv_line, line_vals)
lines.append((line_vals)) lines_current_invoice.append((line_vals))
# Affect accessory costs pro-rata of the value
if total_inv_accessory_costs_cc and total_inv_product_cc:
for ac_line_vals in lines_current_invoice:
ac_line_vals['amount_accessory_cost_company_currency'] = (
total_inv_accessory_costs_cc *
ac_line_vals['amount_company_currency'] /
total_inv_product_cc)
lines += lines_current_invoice
return lines return lines
@api.multi @api.multi
def action_gather(self): def action_gather(self):
self.ensure_one() self.ensure_one()
self.message_post(_("Generate Lines from Invoices"))
self._check_generate_lines() self._check_generate_lines()
self._note = '' self._note = ''
self._uom_refs = { self._uom_refs = {
@@ -489,6 +578,7 @@ class IntrastatProductDeclaration(models.Model):
self._extended = False self._extended = False
self.computation_line_ids.unlink() self.computation_line_ids.unlink()
self.declaration_line_ids.unlink()
lines = self._gather_invoices() lines = self._gather_invoices()
if not lines: if not lines:
@@ -501,7 +591,8 @@ class IntrastatProductDeclaration(models.Model):
self.write({'computation_line_ids': [(0, 0, x) for x in lines]}) self.write({'computation_line_ids': [(0, 0, x) for x in lines]})
if self._note: if self._note:
note_header = '\n\n>>> ' + str(date.today()) + '\n' note_header = '\n\n>>> ' + fields.Datetime.to_string(
fields.Datetime.context_timestamp(self, datetime.now())) + '\n'
self.note = note_header + self._note + (self.note or '') self.note = note_header + self._note + (self.note or '')
result_view = self.env.ref( result_view = self.env.ref(
'intrastat_base.intrastat_result_view_form') 'intrastat_base.intrastat_result_view_form')
@@ -519,15 +610,21 @@ class IntrastatProductDeclaration(models.Model):
return True return True
@api.model @api.model
def _group_line_hashcode_fields(self, computation_line):
return {
'country': computation_line.src_dest_country_id.id or False,
'hs_code_id': computation_line.hs_code_id.id or False,
'intrastat_unit': computation_line.intrastat_unit_id.id or False,
'transaction': computation_line.transaction_id.id or False,
'transport': computation_line.transport_id.id or False,
'region': computation_line.region_id.id or False,
'product_origin_country':
computation_line.product_origin_country_id.id or False,
}
def group_line_hashcode(self, computation_line): def group_line_hashcode(self, computation_line):
hashcode = "%s-%s-%s-%s-%s-%s" % ( fields = self._group_line_hashcode_fields(computation_line)
computation_line.src_dest_country_id.id or False, hashcode = '-'.join([unicode(f) for f in fields.itervalues()])
computation_line.hs_code_id.id or False,
computation_line.intrastat_unit_id.id or False,
computation_line.transaction_id.id or False,
computation_line.transport_id.id or False,
computation_line.region_id.id or False
)
return hashcode return hashcode
@api.multi @api.multi
@@ -535,6 +632,7 @@ class IntrastatProductDeclaration(models.Model):
""" generate declaration lines """ """ generate declaration lines """
self.ensure_one() self.ensure_one()
assert self.valid, 'Computation lines are not valid' assert self.valid, 'Computation lines are not valid'
self.message_post(_("Generate Declaration Lines"))
# Delete existing declaration lines # Delete existing declaration lines
self.declaration_line_ids.unlink() self.declaration_line_ids.unlink()
# Regenerate declaration lines from computation lines # Regenerate declaration lines from computation lines
@@ -557,6 +655,7 @@ class IntrastatProductDeclaration(models.Model):
def generate_xml(self): def generate_xml(self):
""" generate the INTRASTAT Declaration XML file """ """ generate the INTRASTAT Declaration XML file """
self.ensure_one() self.ensure_one()
self.message_post(_("Generate XML Declaration File"))
self._check_generate_xml() self._check_generate_xml()
self._unlink_attachments() self._unlink_attachments()
xml_string = self._generate_xml() xml_string = self._generate_xml()
@@ -565,8 +664,7 @@ class IntrastatProductDeclaration(models.Model):
xml_string, '%s_%s' % (self.type, self.revision)) xml_string, '%s_%s' % (self.type, self.revision))
return self._open_attach_view(attach_id) return self._open_attach_view(attach_id)
else: else:
raise Warning( raise UserError(
_("Programming Error."),
_("No XML File has been generated.")) _("No XML File has been generated."))
@api.multi @api.multi
@@ -625,8 +723,8 @@ class IntrastatProductComputationLine(models.Model):
string='Suppl. Unit', readonly=True, string='Suppl. Unit', readonly=True,
help="Intrastat Supplementary Unit") help="Intrastat Supplementary Unit")
weight = fields.Float( weight = fields.Float(
string='Weight (Kg)', string='Weight',
digits=dp.get_precision('Stock Weight')) digits=dp.get_precision('Stock Weight'), help="Net weight in Kg")
suppl_unit_qty = fields.Float( suppl_unit_qty = fields.Float(
string='Suppl. Unit Qty', string='Suppl. Unit Qty',
digits=dp.get_precision('Product Unit of Measure'), digits=dp.get_precision('Product Unit of Measure'),
@@ -637,6 +735,12 @@ class IntrastatProductComputationLine(models.Model):
help="Amount in company currency to write in the declaration. " help="Amount in company currency to write in the declaration. "
"Amount in company currency = amount in invoice currency " "Amount in company currency = amount in invoice currency "
"converted to company currency with the rate of the invoice date.") "converted to company currency with the rate of the invoice date.")
amount_accessory_cost_company_currency = fields.Float(
string='Accessory Costs',
digits=dp.get_precision('Account'),
help="Amount in company currency of the accessory costs related to "
"this invoice line (by default, these accessory costs are computed "
"at the pro-rata of the amount of each invoice line.")
transaction_id = fields.Many2one( transaction_id = fields.Many2one(
'intrastat.transaction', 'intrastat.transaction',
string='Intrastat Transaction') string='Intrastat Transaction')
@@ -648,6 +752,9 @@ class IntrastatProductComputationLine(models.Model):
transport_id = fields.Many2one( transport_id = fields.Many2one(
'intrastat.transport_mode', 'intrastat.transport_mode',
string='Transport Mode') string='Transport Mode')
product_origin_country_id = fields.Many2one(
'res.country', string='Country of Origin of the Product',
help="Country of origin of the product i.e. product 'made in ____'")
@api.one @api.one
@api.depends('transport_id') @api.depends('transport_id')
@@ -706,7 +813,7 @@ class IntrastatProductDeclarationLine(models.Model):
string='Suppl. Unit', readonly=True, string='Suppl. Unit', readonly=True,
help="Intrastat Supplementary Unit") help="Intrastat Supplementary Unit")
weight = fields.Integer( weight = fields.Integer(
string='Weight (Kg)') string='Weight', help="Net weight in Kg")
suppl_unit_qty = fields.Integer( suppl_unit_qty = fields.Integer(
string='Suppl. Unit Qty', string='Suppl. Unit Qty',
help="Supplementary Units Quantity") help="Supplementary Units Quantity")
@@ -726,6 +833,9 @@ class IntrastatProductDeclarationLine(models.Model):
transport_id = fields.Many2one( transport_id = fields.Many2one(
'intrastat.transport_mode', 'intrastat.transport_mode',
string='Transport Mode') string='Transport Mode')
product_origin_country_id = fields.Many2one(
'res.country', string='Country of Origin of the Product',
help="Country of origin of the product i.e. product 'made in ____'")
@api.model @api.model
def _prepare_grouped_fields(self, computation_line, fields_to_sum): def _prepare_grouped_fields(self, computation_line, fields_to_sum):
@@ -735,7 +845,11 @@ class IntrastatProductDeclarationLine(models.Model):
'hs_code_id': computation_line.hs_code_id.id, 'hs_code_id': computation_line.hs_code_id.id,
'transaction_id': computation_line.transaction_id.id, 'transaction_id': computation_line.transaction_id.id,
'transport_id': computation_line.transport_id.id, 'transport_id': computation_line.transport_id.id,
'region_id': computation_line.region_id.id,
'parent_id': computation_line.parent_id.id, 'parent_id': computation_line.parent_id.id,
'product_origin_country_id':
computation_line.product_origin_country_id.id,
'amount_company_currency': 0.0,
} }
for field in fields_to_sum: for field in fields_to_sum:
vals[field] = 0.0 vals[field] = 0.0
@@ -745,7 +859,6 @@ class IntrastatProductDeclarationLine(models.Model):
fields_to_sum = [ fields_to_sum = [
'weight', 'weight',
'suppl_unit_qty', 'suppl_unit_qty',
'amount_company_currency',
] ]
return fields_to_sum return fields_to_sum
@@ -757,4 +870,14 @@ class IntrastatProductDeclarationLine(models.Model):
for computation_line in computation_lines: for computation_line in computation_lines:
for field in fields_to_sum: for field in fields_to_sum:
vals[field] += computation_line[field] vals[field] += computation_line[field]
vals['amount_company_currency'] += (
computation_line['amount_company_currency'] +
computation_line['amount_accessory_cost_company_currency'])
# round, otherwise odoo with truncate (6.7 -> 6... instead of 7 !)
for field in fields_to_sum:
vals[field] = int(round(vals[field]))
if not vals['weight']:
vals['weight'] = 1
vals['amount_company_currency'] = int(round(
vals['amount_company_currency']))
return vals return vals

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Odoo, Open Source Management Solution # Odoo, Open Source Management Solution

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo
@@ -34,7 +34,8 @@ class IntrastatTransaction(models.Model):
code = fields.Char(string='Code', required=True) code = fields.Char(string='Code', required=True)
description = fields.Text(string='Description') description = fields.Text(string='Description')
display_name = fields.Char( display_name = fields.Char(
compute='_compute_display_name', string="Display Name", readonly=True) compute='_compute_display_name', string="Display Name", readonly=True,
store=True)
company_id = fields.Many2one( company_id = fields.Many2one(
'res.company', string='Company', 'res.company', string='Company',
default=lambda self: self.env['res.company']._company_default_get( default=lambda self: self.env['res.company']._company_default_get(

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo
@@ -30,7 +30,7 @@ class ResCompany(models.Model):
intrastat_incoterm_id = fields.Many2one( intrastat_incoterm_id = fields.Many2one(
'stock.incoterms', 'stock.incoterms',
string='Default incoterm for Intrastat', string='Default Incoterm for Intrastat',
help="International Commercial Terms are a series of " help="International Commercial Terms are a series of "
"predefined commercial terms used in international " "predefined commercial terms used in international "
"transactions.") "transactions.")
@@ -48,7 +48,21 @@ class ResCompany(models.Model):
compute='_compute_intrastat') compute='_compute_intrastat')
intrastat_region_id = fields.Many2one( intrastat_region_id = fields.Many2one(
'intrastat.region', 'intrastat.region',
string='Default Intrastat region') string='Default Intrastat Region')
intrastat_transaction_out_invoice = fields.Many2one(
'intrastat.transaction',
string='Default Intrastat Transaction For Customer Invoice')
intrastat_transaction_out_refund = fields.Many2one(
'intrastat.transaction',
string='Default Intrastat Transaction for Customer Refunds')
intrastat_transaction_in_invoice = fields.Many2one(
'intrastat.transaction',
string='Default Intrastat Transaction For Supplier Invoices')
intrastat_transaction_in_refund = fields.Many2one(
'intrastat.transaction',
string='Default Intrastat Transaction For Supplier Refunds')
intrastat_accessory_costs = fields.Boolean(
string='Include Accessory Costs in Fiscal Value of Product')
@api.model @api.model
def _intrastat_arrivals(self): def _intrastat_arrivals(self):

View File

@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Intrastat Product module for Odoo
# Copyright (C) 2010-2015 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from openerp import models, api
class SaleOrder(models.Model):
_inherit = "sale.order"
@api.model
def _prepare_invoice(self, order, lines):
'''Copy destination country to invoice'''
invoice_vals = super(SaleOrder, self)._prepare_invoice(
order, lines)
invoice_vals['src_dest_country_id'] = \
order.partner_shipping_id.country_id.id or False
invoice_vals['incoterm_id'] = order.incoterm.id or False
return invoice_vals

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Intrastat Product module for Odoo # Intrastat Product module for Odoo

View File

@@ -1,4 +1,4 @@
# -*- encoding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Odoo, Open Source Management Solution # Odoo, Open Source Management Solution
@@ -20,7 +20,7 @@
# #
############################################################################## ##############################################################################
from openerp import models, fields from openerp import models, fields, api
class StockWarehouse(models.Model): class StockWarehouse(models.Model):
@@ -30,10 +30,16 @@ class StockWarehouse(models.Model):
'intrastat.region', 'intrastat.region',
string='Intrastat region') string='Intrastat region')
def get_region_from_location(self, location):
locations = location.search( class StockLocation(models.Model):
[('parent_left', '<=', location.parent_left), _inherit = 'stock.location'
('parent_right', '>=', location.parent_right)])
@api.multi
def get_intrastat_region(self):
self.ensure_one()
locations = self.search(
[('parent_left', '<=', self.parent_left),
('parent_right', '>=', self.parent_right)])
warehouses = self.search( warehouses = self.search(
[('lot_stock_id', 'in', [x.id for x in locations]), [('lot_stock_id', 'in', [x.id for x in locations]),
('region_id', '!=', False)]) ('region_id', '!=', False)])

View File

@@ -10,7 +10,7 @@
<header> <header>
<button name="action_gather" type="object" <button name="action_gather" type="object"
attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('action', '=', 'nihil')]}" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('action', '=', 'nihil')]}"
string="Generate lines from invoices" string="Generate Lines from Invoices"
class="oe_highlight"/> class="oe_highlight"/>
<button name="generate_declaration" type="object" <button name="generate_declaration" type="object"
attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('action', '=', 'nihil')]}" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('action', '=', 'nihil')]}"
@@ -20,7 +20,7 @@
type="object" type="object"
attrs="{'invisible': [('state', '!=', 'draft')]}"/> attrs="{'invisible': [('state', '!=', 'draft')]}"/>
<button name="done" string="Done" type="object" class="oe_highlight" states="draft"/> <button name="done" string="Done" type="object" class="oe_highlight" states="draft"/>
<button name="back2draft" string="Back to draft" type="object" states="done"/> <button name="back2draft" string="Back to Draft" type="object" states="done"/>
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar"/>
</header> </header>
<sheet string="Intrastat Product Declaration"> <sheet string="Intrastat Product Declaration">
@@ -54,62 +54,14 @@
<notebook> <notebook>
<page string="Transactions"> <page string="Transactions">
<group name="computation_lines"> <group name="computation_lines">
<field name="computation_line_ids" nolabel="1"> <field name="computation_line_ids" nolabel="1"/>
<tree string="Transactions">
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="product_id"/>
<field name="hs_code_id"/>
<field name="intrastat_unit_id"/>
<field name="amount_company_currency"/>
<field name="weight" attrs="{'invisible': [('intrastat_unit_id', '!=', False)], 'required': [('intrastat_unit_id', '=', False)]}"/>
<field name="suppl_unit_qty" attrs="{'invisible': [('intrastat_unit_id', '=', False)], 'required': [('intrastat_unit_id', '!=', False)]}"/>
<field name="transaction_id"/>
<field name="invoice_id"/>
<field name="reporting_level" invisible="1"/>
</tree>
<form string="Transactions">
<group col="4" string="Transaction" colspan="4">
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="product_id"/>
<field name="hs_code_id"/>
<field name="intrastat_unit_id"/>
<field name="amount_company_currency" widget="monetary" options="{'currency_field': 'company_currency_id'}"/>
<field name="company_currency_id" invisible="1"/>
<field name="weight"/>
<field name="suppl_unit_qty"/>
<field name="transaction_id"/>
<field name="region_id" invisible="1"/>
<field name="invoice_id"/>
<field name="reporting_level" invisible="1"/>
<field name="transport_id"
attrs="{'required': [('reporting_level', '=', 'extended')], 'invisible': [('reporting_level', '!=', 'extended')]}"/>
<field name="incoterm_id" invisible="1"/>
</group>
</form>
</field>
</group> </group>
</page> </page>
<page string="Declaration Lines"> <page string="Declaration Lines">
<group name="declaration_lines"> <group name="declaration_lines">
<field name="declaration_line_ids" nolabel="1"> <field name="declaration_line_ids" nolabel="1"/>
<tree string="Intrastat Declaration Lines" editable="bottom">
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="hs_code_id"/>
<field name="intrastat_unit_id"/>
<field name="amount_company_currency"/>
<field name="weight"/>
<field name="suppl_unit_qty"/>
<field name="transaction_id"/>
<field name="region_id" invisible="1"/>
<field name="reporting_level" invisible="1"/>
<field name="transport_id"
attrs="{'required': [('reporting_level', '=', 'extended')], 'invisible': [('reporting_level', '!=', 'extended')]}"/>
<field name="incoterm_id" invisible="1"/>
</tree>
</field>
</group> </group>
</page> </page>
<page string="Notes"> <page string="Notes">
<field name="note"/> <field name="note"/>
</page> </page>
@@ -130,7 +82,7 @@
<field name="default" eval="False"/> <field name="default" eval="False"/>
<field name="description">Intrastat Product Declaration Validated</field> <field name="description">Intrastat Product Declaration Validated</field>
</record> </record>
<record id="intrastat_product_declaration_view_tree" model="ir.ui.view"> <record id="intrastat_product_declaration_view_tree" model="ir.ui.view">
<field name="name">intrastat.product.declaration.tree</field> <field name="name">intrastat.product.declaration.tree</field>
<field name="model">intrastat.product.declaration</field> <field name="model">intrastat.product.declaration</field>
@@ -178,5 +130,132 @@
<!-- No menuitem nor action since these are provided by the localization modules --> <!-- No menuitem nor action since these are provided by the localization modules -->
<record id="intrastat_product_computation_line_view_form" model="ir.ui.view">
<field name="name">intrastat.product.computation.line.form</field>
<field name="model">intrastat.product.computation.line</field>
<field name="arch" type="xml">
<form string="Intrastat Transaction">
<group string="Transaction" name="transaction">
<field name="parent_id"
invisible="not context.get('intrastat_product_computation_line_main_view')"/>
<field name="product_id"/>
<field name="hs_code_id"/>
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="amount_company_currency"
widget="monetary"
options="{'currency_field': 'company_currency_id'}"/>
<field name="amount_accessory_cost_company_currency"
widget="monetary"
options="{'currency_field': 'company_currency_id'}"/>
<field name="company_currency_id" invisible="1"/>
<field name="transaction_id"/>
<label for="weight"/>
<div>
<field name="weight" class="oe_inline"/>
<label string=" Kg" class="oe_inline"/>
</div>
<field name="suppl_unit_qty"/>
<field name="intrastat_unit_id"/>
<field name="reporting_level" invisible="1"/>
<field name="transport_id"
attrs="{'required': [('reporting_level', '=', 'extended')], 'invisible': [('reporting_level', '!=', 'extended')]}"/>
<field name="incoterm_id" invisible="1"/>
<field name="region_id" invisible="1"/>
<field name="product_origin_country_id" invisible="1"/>
<field name="invoice_id"/>
</group>
<group string="Declaration" name="declaration">
<field name="declaration_line_id"/>
</group>
</form>
</field>
</record>
<record id="intrastat_product_computation_line_view_tree" model="ir.ui.view">
<field name="name">intrastat.product.computation.line.tree</field>
<field name="model">intrastat.product.computation.line</field>
<field name="arch" type="xml">
<tree string="Transactions">
<field name="parent_id"
invisible="not context.get('intrastat_product_computation_line_main_view')"/>
<field name="product_id"/>
<field name="hs_code_id"/>
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="amount_company_currency"/>
<field name="amount_accessory_cost_company_currency"/>
<field name="transaction_id"/>
<field name="weight"/>
<field name="suppl_unit_qty"
attrs="{'invisible': [('intrastat_unit_id', '=', False)], 'required': [('intrastat_unit_id', '!=', False)]}"/>
<field name="intrastat_unit_id"/>
<field name="region_id" invisible="1"/>
<field name="product_origin_country_id" invisible="1" string="Product C/O"/>
<field name="invoice_id"/>
<field name="reporting_level" invisible="1"/>
</tree>
</field>
</record>
<record id="intrastat_product_declaration_line_view_form" model="ir.ui.view">
<field name="name">intrastat.product.declaration.line.form</field>
<field name="model">intrastat.product.declaration.line</field>
<field name="arch" type="xml">
<form string="Intrastat Declaration Line">
<group name="declaration">
<field name="parent_id"
invisible="not context.get('intrastat_product_declaration_line_main_view')"/>
<field name="hs_code_id"/>
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="amount_company_currency"
widget="monetary"
options="{'currency_field': 'company_currency_id'}"/>
<field name="company_currency_id" invisible="1"/>
<field name="transaction_id"/>
<label for="weight"/>
<div>
<field name="weight" class="oe_inline"/>
<label string=" Kg" class="oe_inline"/>
</div>
<field name="suppl_unit_qty"/>
<field name="intrastat_unit_id"/>
<field name="reporting_level" invisible="1"/>
<field name="transport_id"
attrs="{'required': [('reporting_level', '=', 'extended')], 'invisible': [('reporting_level', '!=', 'extended')]}"/>
<field name="region_id" invisible="1"/>
<field name="incoterm_id" invisible="1"/>
<field name="product_origin_country_id" invisible="1"/>
</group>
<group name="computation" string="Related Transactions">
<field name="computation_line_ids" nolabel="1"/>
</group>
</form>
</field>
</record>
<record id="intrastat_product_declaration_line_view_tree" model="ir.ui.view">
<field name="name">intrastat.product.declaration.line.tree</field>
<field name="model">intrastat.product.declaration.line</field>
<field name="arch" type="xml">
<tree string="Intrastat Declaration Lines">
<field name="parent_id"
invisible="not context.get('intrastat_product_declaration_line_main_view')"/>
<field name="hs_code_id"/>
<field name="src_dest_country_id" domain="[('intrastat', '=', True)]"/>
<field name="amount_company_currency"/>
<field name="transaction_id"/>
<field name="weight"/>
<field name="suppl_unit_qty"/>
<field name="intrastat_unit_id"/>
<field name="reporting_level" invisible="1"/>
<field name="transport_id"
attrs="{'required': [('reporting_level', '=', 'extended')], 'invisible': [('reporting_level', '!=', 'extended')]}"/>
<field name="region_id" invisible="1"/>
<field name="incoterm_id" invisible="1"/>
<field name="product_origin_country_id" invisible="1" string="Product C/O"/>
</tree>
</field>
</record>
</data> </data>
</openerp> </openerp>

View File

@@ -11,11 +11,15 @@
<field name="intrastat" invisible="1"/> <field name="intrastat" invisible="1"/>
<field name="intrastat_arrivals"/> <field name="intrastat_arrivals"/>
<field name="intrastat_dispatches"/> <field name="intrastat_dispatches"/>
<field name="intrastat_transaction_out_invoice"/>
<field name="intrastat_transaction_out_refund"/>
<field name="intrastat_transaction_in_invoice"/>
<field name="intrastat_transaction_in_refund"/>
<field name="intrastat_region_id" domain="[('country_id','=', country_id)]" invisible="1"/> <field name="intrastat_region_id" domain="[('country_id','=', country_id)]" invisible="1"/>
<field name="intrastat_transport_id" <field name="intrastat_transport_id"
attrs="{'required': [('intrastat', '=', 'extended')], 'invisible': [('intrastat', '!=', 'extended')]}"/> attrs="{'required': [('intrastat', '=', 'extended')], 'invisible': [('intrastat', '!=', 'extended')]}"/>
<field name="intrastat_incoterm_id" <field name="intrastat_incoterm_id" invisible="1"/>
attrs="{'required': [('intrastat', '=', 'extended')], 'invisible': [('intrastat', '!=', 'extended')]}"/> <field name="intrastat_accessory_costs" invisible="1"/>
</group> </group>
</field> </field>
</record> </record>

View File

@@ -3,12 +3,12 @@
<data> <data>
<record id="view_warehouse" model="ir.ui.view"> <record id="view_warehouse" model="ir.ui.view">
<field name="name">stock.warehouse.form</field> <field name="name">intrastat.stock.warehouse.form</field>
<field name="model">stock.warehouse</field> <field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse"/> <field name="inherit_id" ref="stock.view_warehouse"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//field[@name='partner_id']" position="after"> <xpath expr="//field[@name='partner_id']" position="after">
<field name="region_id" groups="l10n_be_intrastat_product.group_l10n_be_intrastat_product"/> <field name="region_id"/>
</xpath> </xpath>
</field> </field>
</record> </record>