Merge lp:~therp-nl/new-report-intrastat/8.0-update-l10n_nl_intrastat into lp:new-report-intrastat
- 8.0-update-l10n_nl_intrastat
- Merge into trunk
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 |
Related bugs: |
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.
Commit message
Description of the change
Update dutch intrastat report to 8.0
One change to base module:
- In _check_
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 | # |
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: |
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 compute= dp.get_ precision( 'Account' )
digits_
It should be : dp.get_ precision( 'Account' )
digits=
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 tools.translate import _
from openerp.
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_*