Merge lp:~therp-nl/new-report-intrastat/8.0-update-l10n_nl_intrastat into lp:new-report-intrastat

Proposed by Ronald Portier (Therp)
Status: Merged
Merged at revision: 99
Proposed branch: lp:~therp-nl/new-report-intrastat/8.0-update-l10n_nl_intrastat
Merge into: lp:new-report-intrastat
Diff against target: 495 lines (+214/-181)
2 files modified
intrastat_base/intrastat_common.py (+14/-4)
l10n_nl_intrastat/model/l10n_nl_intrastat.py (+200/-177)
To merge this branch: bzr merge lp:~therp-nl/new-report-intrastat/8.0-update-l10n_nl_intrastat
Reviewer Review Type Date Requested Status
New report intrastat commiters Pending
Review via email: mp+249620@code.launchpad.net

This proposal supersedes a proposal from 2015-02-13.

Description of the change

Update dutch intrastat report to 8.0

One change to base module:

- In _check_generate_lines: check currency on company object directly.

Maybe the check on currency should be replaced by an another check altoghether.
Not all EU countries use the Euro as currency. For instance: UK.

Of course, for the moment we only have modules for France and the Netherlands, that do use the Euro.
But this might change.

To post a comment you must log in.
Revision history for this message
Alexis de Lattre (alexis-via) wrote : Posted in a previous version of this proposal

Thanks for this great work ! It's one step towards the transition to github/OCA

I think that, with the new API, you are not supposed to use
digits_compute=dp.get_precision('Account')

It should be :
digits=dp.get_precision('Account')

By the way, why not use

from openerp.exceptions import Warning

and then:
raise Warning(
_('Cannot generate reports lines in a non-draft state'))

I would also remove
from openerp.tools.translate import _

and update the line:
from openerp import api, models, fields, _

I would also expect @api.model decorators on the methods that set default values... but maybe it works without it ! :)

I'll merge this MP in the main branch and then I'll adapt my MP with the new API for intrastat_base + l10n_fr_*

Revision history for this message
Ronald Portier (Therp) (rportier1962) wrote : Posted in a previous version of this proposal

@Alexis I made a refreshed proposal taking most of your remarks into account.

The default supplying methods indeed work withouth @api.model decorator. This decorator is also not in the example for a default method on the new api documentation.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'intrastat_base/intrastat_common.py'
2--- intrastat_base/intrastat_common.py 2014-04-14 17:41:39 +0000
3+++ intrastat_base/intrastat_common.py 2015-02-13 10:30:49 +0000
4@@ -73,16 +73,26 @@
5 return True
6
7 def _check_generate_lines(self, cr, uid, intrastat, context=None):
8- if not intrastat.company_id.country_id:
9+ """Check wether all requirements are met for generating lines."""
10+ if not intrastat.company_id:
11+ # Should not be possible, but just in case:
12+ raise orm.except_orm(
13+ _('Error :'),
14+ _("Company not yet set on intrastat report.")
15+ )
16+ company_obj = intrastat.company_id # Simplify references
17+ if not company_obj.country_id:
18 raise orm.except_orm(
19 _('Error :'),
20 _("The country is not set on the company '%s'.")
21- % intrastat.company_id.name)
22- if not intrastat.currency_id.name == 'EUR':
23+ % company_obj.name
24+ )
25+ if not company_obj.currency_id.name == 'EUR':
26 raise orm.except_orm(
27 _('Error :'),
28 _("The company currency must be 'EUR', but is currently '%s'.")
29- % intrastat.currency_id.name)
30+ % company_obj.currency_id.name
31+ )
32 return True
33
34 def _check_generate_xml(self, cr, uid, intrastat, context=None):
35
36=== modified file 'l10n_nl_intrastat/model/l10n_nl_intrastat.py'
37--- l10n_nl_intrastat/model/l10n_nl_intrastat.py 2013-07-21 14:00:37 +0000
38+++ l10n_nl_intrastat/model/l10n_nl_intrastat.py 2015-02-13 10:30:49 +0000
39@@ -1,12 +1,14 @@
40 # -*- encoding: utf-8 -*-
41+"""Define intrastat report (ICP) for dutch tax authorities."""
42 ##############################################################################
43 #
44 # OpenERP Report intrastat for NL
45-# Copyright (C) 2012 - 2013 Therp BV <http://therp.nl>
46+# Copyright (C) 2012 - 2015 Therp BV <http://therp.nl>
47 #
48 # Based on and containing code snippets from lp:new-report-intrastat
49 # by Alexis de Lattre <alexis.delattre@akretion.com>,
50-# Copyright (C) 2010-2011 Akretion (http://www.akretion.com). All Rights Reserved
51+# Copyright (C) 2010-2011 Akretion (http://www.akretion.com).
52+# All Rights Reserved
53 #
54 # This program is free software: you can redistribute it and/or modify
55 # it under the terms of the GNU Affero General Public License as
56@@ -22,189 +24,205 @@
57 # along with this program. If not, see <http://www.gnu.org/licenses/>.
58 #
59 ##############################################################################
60-
61-from openerp.osv import orm, fields
62+from openerp import api, models, fields, _
63+from openerp.exceptions import Warning
64 from datetime import datetime
65 from dateutil.relativedelta import relativedelta
66-from openerp.tools.translate import _
67 from openerp.addons.decimal_precision import decimal_precision as dp
68
69
70-class l10n_nl_report_intrastat(orm.Model):
71+class ReportIntrastat(models.Model):
72+ """Define intrastat report (ICP) for dutch tax authorities."""
73 _name = "l10n_nl.report.intrastat"
74 _description = "Declaration of intracommunautary transactions (ICP)"
75 _order = "period_id desc"
76 _rec_name = 'period_id'
77
78- _columns = {
79- 'line_ids': fields.one2many(
80- 'l10n_nl.report.intrastat.line',
81- 'report_id',
82- 'ICP line',
83- readonly=True),
84- 'period_id': fields.many2one(
85- 'account.period',
86- 'Period',
87- states={'done': [('readonly', True)]}),
88- 'company_id': fields.many2one(
89- 'res.company', 'Company', required=True,
90- states={'done': [('readonly',True)]},
91- help="Related company."),
92- 'total_amount': fields.float(
93- 'Total amount',
94- digits_compute=dp.get_precision('Account'),
95- readonly=True,
96- help="Total amount in company currency of the declaration."),
97- 'state' : fields.selection(
98- [
99- ('draft','Draft'),
100- ('done','Done'),
101- ], 'State', select=True, readonly=True,
102- help=("State of the declaration. When the state is set to 'Done', "
103- "the parameters become read-only.")),
104- 'date_done' : fields.date(
105- 'Date done', readonly=True,
106- help=("Last date when the intrastat declaration was converted to "
107- "'Done' state.")),
108- 'notes' : fields.text(
109- 'Notes',
110- help="You can add some comments here if you want."),
111- }
112-
113- def get_period(self, cr, uid, context=None):
114+ def _default_period_id(self):
115 """
116 By default, take the previous period relative to the
117 current date. Courtesy of Alexis.
118 """
119+ period_model = self.env['account.period']
120 date = datetime.strftime(
121 datetime.today() + relativedelta(day=1, months=-1), '%Y-%m-%d')
122- period_ids = self.pool.get('account.period').find(
123- cr, uid, dt=date, context=context)
124+ period_ids = period_model.find(dt=date)
125 return period_ids and period_ids[0] or False
126
127- _defaults = {
128- 'period_id': get_period,
129- 'state': 'draft',
130- 'company_id': lambda self, cr, uid, context: \
131- self.pool.get('res.users').browse(
132- cr, uid, uid, context=context).company_id.id,
133- }
134-
135- def set_draft(self, cr, uid, ids, context=None):
136- return self.write(
137- cr, uid, ids, {'state': 'draft'}, context=context)
138-
139- def generate_lines(self, cr, uid, ids, context=None):
140+ def _default_company_id(self):
141+ """Default company is active user company."""
142+ return self.env.user.company_id.id
143+
144+ period_id = fields.Many2one(
145+ string='Period',
146+ comodel_name='account.period',
147+ states={'done': [('readonly', True)]},
148+ default=_default_period_id,
149+ )
150+ company_id = fields.Many2one(
151+ string='Company',
152+ comodel_name='res.company',
153+ required=True,
154+ states={'done': [('readonly', True)]},
155+ help="Related company.",
156+ default=_default_company_id,
157+ )
158+ total_amount = fields.Float(
159+ string='Total amount',
160+ readonly=True,
161+ help="Total amount in company currency of the declaration.",
162+ digits=dp.get_precision('Account'),
163+ )
164+ state = fields.Selection(
165+ string='State',
166+ selection=[
167+ ('draft', 'Draft'),
168+ ('done', 'Done'),
169+ ],
170+ default='draft',
171+ readonly=True,
172+ help=(
173+ "State of the declaration. When the state is set to 'Done', "
174+ "the parameters become read-only."
175+ ),
176+ )
177+ date_done = fields.Date(
178+ string='Date done',
179+ readonly=True,
180+ help=(
181+ "Last date when the intrastat declaration was converted to "
182+ "'Done' state."
183+ ),
184+ )
185+ notes = fields.Text(
186+ string='Notes',
187+ help="You can add some comments here if you want.",
188+ )
189+ line_ids = fields.One2many(
190+ string='ICP line',
191+ comodel_name='l10n_nl.report.intrastat.line',
192+ inverse_name='report_id',
193+ readonly=True,
194+ )
195+
196+ @api.one
197+ def set_draft(self):
198+ """
199+ Reset report state to draft.
200+ """
201+ return self.write({'state': 'draft'})
202+
203+ @api.one
204+ def generate_lines(self):
205 """
206 Collect the data lines for the given report.
207 Unlink any existing lines first.
208 """
209- report_line_obj = self.pool.get('l10n_nl.report.intrastat.line')
210- currency_obj = self.pool.get('res.currency')
211- invoice_obj = self.pool.get('account.invoice')
212- invoice_line_obj = self.pool.get('account.invoice.line')
213+ # Generating lines is only allowed for report in draft state:
214+ if self.state != 'draft':
215+ raise Warning(_(
216+ 'Cannot generate reports lines in a non-draft state'))
217+ # Other models:
218+ report_line_model = self.env['l10n_nl.report.intrastat.line']
219+ currency_model = self.env['res.currency']
220+ invoice_model = self.env['account.invoice']
221+ invoice_line_model = self.env['account.invoice.line']
222
223 total_amount = 0.0
224 partner_amounts_map = {}
225- localcontext = context and context.copy() or {}
226+ # Check wether all configuration done to generate report
227+ self.env['report.intrastat.common']._check_generate_lines(self)
228
229 # Remove existing lines
230- line_ids = report_line_obj.search(
231- cr, uid, [('report_id', '=', ids[0])], context=context)
232- report_line_obj.unlink(cr, uid, line_ids, context=context)
233- report = self.browse(cr, uid, ids[0], context=context)
234-
235- if report.state != 'draft':
236- raise orm.except_orm(
237- _('Error'),
238- _('Cannot generate reports lines in a non-draft state'))
239-
240+ for line_obj in self.line_ids:
241+ line_obj.unlink()
242+ # Define search for invoices for period and company:
243+ company_obj = self.company_id # simplify access
244 invoice_domain = [
245- ('type', 'in', ('out_invoice', 'out_refund')),
246- ('period_id', '=', report.period_id.id),
247- ('state', 'in', ('open', 'paid')),
248- ('company_id', '=', report.company_id.id),
249- ('partner_id.country_id.id', '!=',
250- report.company_id.country_id.id),
251- ]
252-
253+ ('type', 'in', ('out_invoice', 'out_refund')),
254+ ('period_id', '=', self.period_id.id),
255+ ('state', 'in', ('open', 'paid')),
256+ ('company_id', '=', company_obj.id),
257+ ]
258 # Signal invoices without a country
259- invalid_invoice_ids = invoice_obj.search(
260- cr, uid,
261- invoice_domain + [
262- ('partner_id.country_id', '=', False)],
263- context=context)
264- if invalid_invoice_ids:
265- invoices = invoice_obj.read(
266- cr, uid,
267- invalid_invoice_ids, ['partner_id'],
268- context=context)
269- raise orm.except_orm(
270- _('Error'),
271- _("Missing country on the invoice addresses of the following "
272- "partners:\n%s") % (
273- "\n".join([inv['partner_id'][1] for inv in invoices])
274- ))
275-
276- invoice_ids = invoice_obj.search(
277- cr, uid,
278- invoice_domain + [
279- ('partner_id.country_id.intrastat', '=', True)],
280- context=context)
281- invoice_line_ids = invoice_line_obj.search(
282- cr, uid, [('invoice_id', 'in', invoice_ids)], context=context)
283+ # (Should not happen, as country_id on parter is set to required)
284+ invalid_invoices = invoice_model.search_read(
285+ domain=invoice_domain + [('partner_id.country_id', '=', False)],
286+ fields=['partner_id']
287+ )
288+ if invalid_invoices:
289+ raise Warning(
290+ _(
291+ "Missing country on the invoice addresses of the"
292+ " following partners:\n%s"
293+ ) % "\n".join(
294+ inv['partner_id'][1] for inv in invalid_invoices)
295+ )
296+ # Search invoices that need intrastat reporting:
297+ invoice_records = invoice_model.search(
298+ invoice_domain + [
299+ ('partner_id.country_id.intrastat', '=', True),
300+ ('partner_id.country_id.id', '!=', company_obj.country_id.id),
301+ ]
302+ )
303+ invoice_ids = [inv['id'] for inv in invoice_records]
304+ invoice_line_records = invoice_line_model.search(
305+ [('invoice_id', 'in', invoice_ids)])
306
307 # Gather amounts from invoice lines
308- for line in invoice_line_obj.browse(
309- cr, uid, invoice_line_ids, context=context):
310+ for line in invoice_line_records:
311+ # Ignore invoiceline if taxes should not be included in intrastat:
312 if any(
313- tax.exclude_from_intrastat_if_present
314- for tax in line.invoice_line_tax_id):
315- continue
316- localcontext['date'] = line.invoice_id.date_invoice
317- commercial_partner_id = line.invoice_id.partner_id.commercial_partner_id.id
318+ tax.exclude_from_intrastat_if_present
319+ for tax in line.invoice_line_tax_id):
320+ continue
321+ # Report is per commercial partner:
322+ commercial_partner_id = (
323+ line.invoice_id.partner_id.commercial_partner_id.id)
324 if commercial_partner_id not in partner_amounts_map:
325 partner_amounts_map[commercial_partner_id] = {
326 'amount_product': 0.0,
327 'amount_service': 0.0,
328- }
329+ }
330 amounts = partner_amounts_map[commercial_partner_id]
331- if line.product_id and (
332- line.product_id.type == 'service'
333- or line.product_id.is_accessory_cost):
334+ # Determine product or service:
335+ if (line.product_id
336+ and (
337+ line.product_id.type == 'service'
338+ or line.product_id.is_accessory_cost)):
339 amount_type = 'amount_service'
340 else:
341 amount_type = 'amount_product'
342 sign = line.invoice_id.type == 'out_refund' and -1 or 1
343 amount = sign * line.price_subtotal
344- if (line.invoice_id.currency_id
345- and line.invoice_id.currency_id.id !=
346- report.company_id.currency_id.id):
347- amount = currency_obj.compute(
348- cr, uid, line.invoice_id.currency_id.id,
349- report.company_id.currency_id.id,
350- amount, context=localcontext)
351- amounts[amount_type] += amount
352- total_amount += amount
353-
354+ # Convert currency amount if necessary:
355+ line_currency_obj = line.invoice_id.currency_id # simplify
356+ invoice_date = line.invoice_id.date_invoice
357+ if (line_currency_obj
358+ and line_currency_obj.id != company_obj.currency_id.id):
359+ amount = (
360+ line_currency_obj.with_context(date=invoice_date).compute(
361+ amount, company_obj.currency_id, round=True)
362+ )
363+ # Accumulate totals:
364+ amounts[amount_type] += amount # per partner and type
365+ total_amount += amount # grand total
366+
367 # Create report lines
368 for (partner_id, vals) in partner_amounts_map.items():
369 if not (vals['amount_service'] or vals['amount_product']):
370 continue
371 vals.update({
372- 'partner_id': partner_id,
373- 'report_id': report.id
374- })
375- report_line_obj.create(
376- cr, uid, vals, context=context)
377+ 'partner_id': partner_id,
378+ 'report_id': self.id
379+ })
380+ report_line_model.create(vals)
381
382- return self.write(
383- cr, uid, report.id, {
384- 'total_amount': total_amount,
385- 'date_done': fields.date.context_today(
386- self, cr, uid, context=context),
387- 'state': 'done',
388- }, context=context)
389+ return self.write({
390+ 'total_amount': total_amount,
391+ 'date_done': fields.Date.today(),
392+ 'state': 'done',
393+ })
394
395 def unlink(self, cr, uid, ids, context=None):
396 """
397@@ -214,53 +232,58 @@
398 return True
399 if isinstance(ids, (int, long)):
400 ids = [ids]
401- if self.search(
402+ non_draft_ids = self.search(
403 cr, uid,
404 [('id', 'in', ids), ('state', '!=', 'draft')],
405- context=context):
406- raise orm.except_orm(
407- _('Error'),
408- _('Cannot remove IPC reports in a non-draft state'))
409- return super(l10n_nl_report_intrastat, self).unlink(
410+ context=context
411+ )
412+ if non_draft_ids:
413+ raise Warning(_('Cannot remove IPC reports in a non-draft state'))
414+ return super(ReportIntrastat, self).unlink(
415 cr, uid, ids, context=context)
416
417
418-class l10n_nl_report_intrastat_line(orm.Model):
419+class ReportIntrastatLine(models.Model):
420+ """Lines for dutch ICP report."""
421 _name = "l10n_nl.report.intrastat.line"
422 _description = "ICP report line"
423 _order = "report_id, country_code"
424 _rec_name = 'partner_id'
425
426- _columns = {
427- 'report_id': fields.many2one(
428- 'l10n_nl.report.intrastat',
429- 'ICP report',
430- readonly=True,
431- required=True,
432- ondelete="CASCADE"),
433- 'partner_id': fields.many2one(
434- 'res.partner', 'Partner',
435- readonly=True,
436- required=True),
437- 'vat': fields.related(
438- 'partner_id', 'vat',
439- type='char', size=32,
440- string='VAT',
441- store=True,
442- readonly=True),
443- 'country_code': fields.related(
444- 'partner_id', 'country', 'code',
445- type='char', size=2,
446- string='Country Code',
447- store=True,
448- readonly=True,
449- ),
450- 'amount_product': fields.float(
451- 'Amount products',
452- digits_compute=dp.get_precision('Account'),
453- readonly=True),
454- 'amount_service': fields.float(
455- 'Amount services',
456- digits_compute=dp.get_precision('Account'),
457- readonly=True),
458- }
459+ report_id = fields.Many2one(
460+ string='ICP report',
461+ comodel_name='l10n_nl.report.intrastat',
462+ readonly=True,
463+ required=True,
464+ ondelete="CASCADE"
465+ )
466+ partner_id = fields.Many2one(
467+ string='Partner',
468+ comodel_name='res.partner',
469+ readonly=True,
470+ required=True,
471+ )
472+ vat = fields.Char(
473+ string='VAT',
474+ related='partner_id.vat',
475+ store=True,
476+ readonly=True,
477+ )
478+ country_code = fields.Char(
479+ string='Country Code',
480+ related='partner_id.country_id.code',
481+ store=True,
482+ readonly=True,
483+ )
484+ amount_product = fields.Float(
485+ string='Amount products',
486+ readonly=True,
487+ digits=dp.get_precision('Account'),
488+ )
489+ amount_service = fields.Float(
490+ string='Amount services',
491+ readonly=True,
492+ digits=dp.get_precision('Account'),
493+ )
494+
495+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

Subscribers

People subscribed via source and target branches