diff --git a/easy_my_coop_taxshelter_report/__init__.py b/easy_my_coop_taxshelter_report/__init__.py index 9a7e03e..0650744 100644 --- a/easy_my_coop_taxshelter_report/__init__.py +++ b/easy_my_coop_taxshelter_report/__init__.py @@ -1 +1 @@ -from . import models \ No newline at end of file +from . import models diff --git a/easy_my_coop_taxshelter_report/__manifest__.py b/easy_my_coop_taxshelter_report/__manifest__.py new file mode 100644 index 0000000..e30b638 --- /dev/null +++ b/easy_my_coop_taxshelter_report/__manifest__.py @@ -0,0 +1,33 @@ +# Copyright 2013-2018 Open Architects Consulting SPRL. +# Copyright 2018 Coop IT Easy SCRLfs () +# - Houssine Bakkali +# - Elouan Le Bars +# - Rémy Taymans +# - Manuel Claeys Bouuaert +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + # todo check ir model access + "name": "Easy My Coop tax shelter report", + "version": "12.0.1.0.1", + "depends": ["easy_my_coop"], + "author": "Houssine BAKKALI ", + "category": "Cooperative management", + "website": "www.coopiteasy.be", + "license": "AGPL-3", + "description": """ + This module allows you to create a fiscal declaration year and to print + tax shelter declaration for each cooperator. + """, + "data": [ + "security/ir.model.access.csv", + "reports/tax_shelter_report.xml", + "reports/tax_shelter_subscription_report.xml", + "reports/tax_shelter_shares_report.xml", + "views/tax_shelter_declaration_view.xml", + "data/mail_template_data.xml", + "data/scheduler_data.xml", + ], + "demo": ["demo/tax_shelter_demo.xml"], + "installable": True, +} diff --git a/easy_my_coop_taxshelter_report/__openerp__.py b/easy_my_coop_taxshelter_report/__openerp__.py deleted file mode 100644 index 8e35b61..0000000 --- a/easy_my_coop_taxshelter_report/__openerp__.py +++ /dev/null @@ -1,44 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# Copyright (C) 2013-2017 Open Architects Consulting SPRL. -# Copyright (C) 2018- Coop IT Easy SCRLfs. -# -# 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 . -# -############################################################################## -{ - # todo check ir model access - "name": "Easy My Coop tax shelter report", - "version": "1.0", - "depends": ["easy_my_coop"], - "author": "Houssine BAKKALI ", - "category": "Cooperative management", - 'website': "www.coopiteasy.be", - "license": "AGPL-3", - "description": """ - This module allows to create a fiscal declaration year and to print - tax shelter declaration each cooperator - """, - 'data': [ - 'security/ir.model.access.csv', - 'reports/tax_shelter_report.xml', - 'reports/tax_shelter_subscription_report.xml', - 'reports/tax_shelter_shares_report.xml', - 'views/tax_shelter_declaration_view.xml', - 'data/mail_template_data.xml', - 'data/scheduler_data.xml', - ], - 'installable': True, -} diff --git a/easy_my_coop_taxshelter_report/data/mail_template_data.xml b/easy_my_coop_taxshelter_report/data/mail_template_data.xml index d896022..6bd5e8d 100644 --- a/easy_my_coop_taxshelter_report/data/mail_template_data.xml +++ b/easy_my_coop_taxshelter_report/data/mail_template_data.xml @@ -1,56 +1,57 @@ - - - - - Tax Shelter Certificate - Send By Email - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - Tax Shelter Certificate - ${object.partner_id.id} - ${(object.company_id.coop_email_contact or object.user_id.email)|safe} - - - ${object.partner_id.lang} - - -

Hello ${object.partner_id.name},

- -

You have subscribed to some shares of ${object.company_id.name} on ${object.declaration_id.fiscal_year}. - You can benefit from the tax shelter, it means a tax reduction of ${object.declaration_id.tax_shelter_percentage} percent on the invested amount. - For this you will find in attachments the documents certifying that you've suscribed to ${object.company_id.name} shares

-

A dedicated FAQ is coming soon on ${object.company_id.website}.

-

For any additional questions, please contact ${object.company_id.coop_email_contact}

-

Sustainably your,

-

${object.company_id.name}.

- - % if object.company_id.street: - ${object.company_id.street} - % endif - % if object.company_id.street2: - ${object.company_id.street2}
- % endif - % if object.company_id.city or object.company_id.zip: - ${object.company_id.zip} ${object.company_id.city}
- % endif - % if object.company_id.country_id: - ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
- % endif - % if object.company_id.phone: - Phone:  ${object.company_id.phone} - % endif - - % if object.company_id.website: - - %endif - -
- -
- - ]]>
-
-
-
+ + + + + + Tax Shelter Certificate - Send By Email + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + Tax Shelter Certificate + ${object.partner_id.id} + ${(object.company_id.coop_email_contact or object.user_id.email)|safe} + + + ${object.partner_id.lang} + + +

Hello ${object.partner_id.name},

+ +

You have subscribed to some shares of ${object.company_id.name} on ${object.declaration_id.fiscal_year}. + You can benefit from the tax shelter, it means a tax reduction of ${object.declaration_id.tax_shelter_percentage} percent on the invested amount. + For this you will find in attachments the documents certifying that you've suscribed to ${object.company_id.name} shares

+

A dedicated FAQ is coming soon on ${object.company_id.website}.

+

For any additional questions, please contact ${object.company_id.coop_email_contact}

+

Sustainably your,

+

${object.company_id.name}.

+ + % if object.company_id.street: + ${object.company_id.street} + % endif + % if object.company_id.street2: + ${object.company_id.street2}
+ % endif + % if object.company_id.city or object.company_id.zip: + ${object.company_id.zip} ${object.company_id.city}
+ % endif + % if object.company_id.country_id: + ${object.company_id.state_id and ('%s, ' % object.company_id.state_id.name) or ''} ${object.company_id.country_id.name or ''}
+ % endif + % if object.company_id.phone: + Phone:  ${object.company_id.phone} + % endif + + % if object.company_id.website: + + %endif + +
+ +
+ + ]]>
+
+
+
diff --git a/easy_my_coop_taxshelter_report/data/scheduler_data.xml b/easy_my_coop_taxshelter_report/data/scheduler_data.xml index 6b191f6..109f186 100644 --- a/easy_my_coop_taxshelter_report/data/scheduler_data.xml +++ b/easy_my_coop_taxshelter_report/data/scheduler_data.xml @@ -1,16 +1,15 @@ - - - - - Tax shelter mail batch mail - - 1 - hours - -1 - - - - - - - \ No newline at end of file + + + + + Tax shelter mail batch mail + + code + model.batch_send_tax_shelter_certificate() + 1 + hours + -1 + + + + diff --git a/easy_my_coop_taxshelter_report/demo/tax_shelter_demo.xml b/easy_my_coop_taxshelter_report/demo/tax_shelter_demo.xml new file mode 100644 index 0000000..e526bd5 --- /dev/null +++ b/easy_my_coop_taxshelter_report/demo/tax_shelter_demo.xml @@ -0,0 +1,40 @@ + + + + + 2019 + 2018 + 2018-01-01 + 2018-12-31 + January + December + 45 + validated + 143875.00 + 0.00 + + + + 1 + + + sent + + + + + + 25 + 12 + + + subscribed + + + + + + diff --git a/easy_my_coop_taxshelter_report/i18n/fr_BE.po b/easy_my_coop_taxshelter_report/i18n/fr_BE.po index cab1ccc..b0786ad 100644 --- a/easy_my_coop_taxshelter_report/i18n/fr_BE.po +++ b/easy_my_coop_taxshelter_report/i18n/fr_BE.po @@ -572,12 +572,12 @@ msgid "Tax shelter eligible" msgstr "Eligible au Tax Shelter" #. module: easy_my_coop_taxshelter_report -#: model:ir.actions.report.xml,name:easy_my_coop_taxshelter_report.action_tax_shelter_shares_report +#: model:ir.actions.report,name:easy_my_coop_taxshelter_report.action_tax_shelter_shares_report msgid "Tax shelter shares report" msgstr "Attestation de parts Tax Shelter" #. module: easy_my_coop_taxshelter_report -#: model:ir.actions.report.xml,name:easy_my_coop_taxshelter_report.action_tax_shelter_subscription_report +#: model:ir.actions.report,name:easy_my_coop_taxshelter_report.action_tax_shelter_subscription_report msgid "Tax shelter subscription report" msgstr "Attestation de souscription Tax Shelter" @@ -593,7 +593,7 @@ msgstr "Montant total" #. module: easy_my_coop_taxshelter_report #: model:ir.model.fields,field_description:easy_my_coop_taxshelter_report.field_tax_shelter_certificate_total_amount_eligible -msgid "Total amount eligible To Tax shelter" +msgid "Total eligible amount" msgstr "Montant total éligible au Tax Shelter" #. module: easy_my_coop_taxshelter_report diff --git a/easy_my_coop_taxshelter_report/models/__init__.py b/easy_my_coop_taxshelter_report/models/__init__.py index ab4f736..e60273b 100644 --- a/easy_my_coop_taxshelter_report/models/__init__.py +++ b/easy_my_coop_taxshelter_report/models/__init__.py @@ -1,2 +1,2 @@ -from . import tax_shelter_declaration -from . import mail_template \ No newline at end of file +from . import tax_shelter_declaration +from . import mail_template diff --git a/easy_my_coop_taxshelter_report/models/mail_template.py b/easy_my_coop_taxshelter_report/models/mail_template.py index 4d44e0a..6495bd1 100644 --- a/easy_my_coop_taxshelter_report/models/mail_template.py +++ b/easy_my_coop_taxshelter_report/models/mail_template.py @@ -1,60 +1,56 @@ -from odoo import api, models - - -class MailTemplate(models.Model): - _inherit = "mail.template" - -# def init(self): -# for template_id in EMAIL_TEMPLATE_IDS: -# mail_template = self.env.ref(template_id) -# mail_template.easy_my_coop = True - - @api.multi - def send_mail_with_multiple_attachments(self, res_id, - additional_attachments, - force_send=False, - raise_exception=False): - """Generates a new mail message for the given template and record, - and schedules it for delivery through the ``mail`` - module's scheduler. - - :param int res_id: id of the record to render the template with - (model is taken from the template) - :param bool force_send: if True, the generated mail.message is - immediately sent after being created, as if the scheduler - was executed for this message only. - :returns: id of the mail.message that was created - """ - self.ensure_one() - Mail = self.env['mail.mail'] - # TDE FIXME: should remove dfeault_type from context - Attachment = self.env['ir.attachment'] - - # create a mail_mail based on values, without attachments - values = self.generate_email(res_id) - values['recipient_ids'] = [(4, pid) for pid in values.get('partner_ids', list())] - attachment_ids = values.pop('attachment_ids', []) - attachments = values.pop('attachments', []) - # add a protection against void email_from - if 'email_from' in values and not values.get('email_from'): - values.pop('email_from') - mail = Mail.create(values) - - # manage attachments - attachments.extend(additional_attachments) - for attachment in attachments: - attachment_data = { - 'name': attachment[0], - 'datas_fname': attachment[0], - 'datas': attachment[1], - 'res_model': 'mail.message', - 'res_id': mail.mail_message_id.id, - } - attachment_ids.append(Attachment.create(attachment_data).id) - if attachment_ids: - values['attachment_ids'] = [(6, 0, attachment_ids)] - mail.write({'attachment_ids': [(6, 0, attachment_ids)]}) - - if force_send: - mail.send(raise_exception=raise_exception) - return mail.id # TDE CLEANME: return mail + api.returns ? +from odoo import api, models + + +class MailTemplate(models.Model): + _inherit = "mail.template" + + @api.multi + def send_mail_with_multiple_attachments( + self, res_id, additional_attachments, force_send=False, raise_exception=False + ): + """Generates a new mail message for the given template and record, + and schedules it for delivery through the ``mail`` + module's scheduler. + + :param int res_id: id of the record to render the template with + (model is taken from the template) + :param bool force_send: if True, the generated mail.message is + immediately sent after being created, as if the scheduler + was executed for this message only. + :returns: id of the mail.message that was created + """ + self.ensure_one() + Mail = self.env["mail.mail"] + # TDE FIXME: should remove dfeault_type from context + Attachment = self.env["ir.attachment"] + + # create a mail_mail based on values, without attachments + values = self.generate_email(res_id) + values["recipient_ids"] = [ + (4, pid) for pid in values.get("partner_ids", list()) + ] + attachment_ids = values.pop("attachment_ids", []) + attachments = values.pop("attachments", []) + # add a protection against void email_from + if "email_from" in values and not values.get("email_from"): + values.pop("email_from") + mail = Mail.create(values) + + # manage attachments + attachments.extend(additional_attachments) + for attachment in attachments: + attachment_data = { + "name": attachment[0], + "datas_fname": attachment[0], + "datas": attachment[1], + "res_model": "mail.message", + "res_id": mail.mail_message_id.id, + } + attachment_ids.append(Attachment.create(attachment_data).id) + if attachment_ids: + values["attachment_ids"] = [(6, 0, attachment_ids)] + mail.write({"attachment_ids": [(6, 0, attachment_ids)]}) + + if force_send: + mail.send(raise_exception=raise_exception) + return mail.id # TDE CLEANME: return mail + api.returns ? diff --git a/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py b/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py index 774a38a..efe5803 100644 --- a/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py +++ b/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py @@ -1,363 +1,463 @@ -# -*- coding: utf-8 -*- -import base64 - -from openerp import api, fields, models - -TYPE_MAP = { - 'subscription': 'subscribed', - 'transfer': 'transfered', - 'sell_back': 'resold' -} - -REPORT_DIC = { - 'subscription': ( - 'easy_my_coop_taxshelter_report.tax_shelter_subscription_report', - 'Tax Shelter Subscription' - ), - 'shares': ( - 'easy_my_coop_taxshelter_report.tax_shelter_shares_report', - 'Tax Shelter Shares' - ) -} - - -class TaxShelterDeclaration(models.Model): - - _name = "tax.shelter.declaration" - - name = fields.Char(string='Declaration year', required=True) - fiscal_year = fields.Char(String="Fiscal year", required=True) - tax_shelter_certificates = fields.One2many('tax.shelter.certificate', - 'declaration_id', - string='Tax shelter certificates', - readonly=True) - date_from = fields.Date(string='Date from', required=True) - date_to = fields.Date(string='Date to', required=True) - month_from = fields.Char(String='Month from', required=True) - month_to = fields.Char(String='Month to', required=True) - tax_shelter_percentage = fields.Selection([('30', '30%'), - ('45', '45%')], - string='Tax Shelter percentage', - required=True) - state = fields.Selection([('draft', 'Draft'), - ('computed', 'Computed'), - ('validated', 'Validated')], - string='State', required=True, default="draft") - company_id = fields.Many2one('res.company', string='Company', - required=True, - change_default=True, readonly=True, - default=lambda self: self.env['res.company']._company_default_get()) - tax_shelter_capital_limit = fields.Float(string="Tax shelter capital limit", - required=True) - previously_subscribed_capital = fields.Float(String="Capital previously subscribed", - readonly=True) - excluded_cooperator = fields.Many2many('res.partner', - string="Excluded cooperator", - domain=[('cooperator', '=', True)], - help="If these cooperator have" - " subscribed share during the time" - " frame of this Tax Shelter " - "Declaration. They will be marked " - "as non eligible") - - def _excluded_from_declaration(self, entry): - if entry.date >= self.date_from and entry.date <= self.date_to: - declaration = self - else: - declaration = self.search([('date_from', '<=', entry.date), - ('date_to', '>=', entry.date)]) - if entry.partner_id.id in declaration.excluded_cooperator.ids: - return True - return False - - def _prepare_line(self, certificate, entry, ongoing_capital_sub, excluded): - line_vals = {} - line_vals['tax_shelter_certificate'] = certificate.id - line_vals['share_type'] = entry.share_product_id.id - line_vals['share_short_name'] = entry.share_short_name - line_vals['share_unit_price'] = entry.share_unit_price - line_vals['quantity'] = entry.quantity - line_vals['transaction_date'] = entry.date - line_vals['type'] = TYPE_MAP[entry.type] - if entry.type == 'subscription': - if not excluded: - capital_after_sub = ongoing_capital_sub + entry.total_amount_line - else: - capital_after_sub = ongoing_capital_sub - line_vals['capital_before_sub'] = ongoing_capital_sub - line_vals['capital_after_sub'] = capital_after_sub - line_vals['capital_limit'] = self.tax_shelter_capital_limit - if ongoing_capital_sub < self.tax_shelter_capital_limit and not excluded: - line_vals['tax_shelter'] = True - return line_vals - - def _compute_certificates(self, entries, partner_certificate): - ongoing_capital_sub = 0.0 - for entry in entries: - certificate = partner_certificate.get(entry.partner_id.id, False) - - if not certificate: - # create a certificate for this cooperator - cert_vals = {} - cert_vals['declaration_id'] = self.id - cert_vals['partner_id'] = entry.partner_id.id - cert_vals['cooperator_number'] = entry.partner_id.cooperator_register_number - certificate = self.env['tax.shelter.certificate'].create(cert_vals) - partner_certificate[entry.partner_id.id] = certificate - excluded = self._excluded_from_declaration(entry) - line_vals = self._prepare_line(certificate, entry, ongoing_capital_sub, excluded) - certificate.write({'lines': [(0, 0, line_vals)]}) - - if entry.type == 'subscription' and not excluded: - ongoing_capital_sub += entry.total_amount_line - - return partner_certificate - - @api.one - def compute_declaration(self): - entries = self.env['subscription.register'].search([ - ('partner_id.is_company', '=', False), - ('date', '<=', self.date_to), - ('type', 'in', ['subscription', - 'sell_back', - 'transfer']) - ]) - - subscriptions = entries.filtered((lambda r: r.type == 'subscription' and r.date < self.date_from)) #noqa - cap_prev_sub = 0.0 - for subscription in subscriptions: - cap_prev_sub += subscription.total_amount_line - - self.previously_subscribed_capital = cap_prev_sub - - partner_cert = {} - - partner_cert = self._compute_certificates(entries, partner_cert) - - self.state = 'computed' - - @api.one - def validate_declaration(self): - self.tax_shelter_certificates.write({'state': 'validated'}) - self.state = 'validated' - - @api.one - def reset_declaration(self): - if not self.state == 'validated': - self.tax_shelter_certificates.unlink() - self.state = 'draft' - - -class TaxShelterCertificate(models.Model): - _name = "tax.shelter.certificate" - - _order = "cooperator_number asc" - - cooperator_number = fields.Integer(string='Cooperator number', - required=True, - readonly=True) - partner_id = fields.Many2one('res.partner', string='Cooperator', - required=True, readonly=True) - state = fields.Selection([('draft', 'Draft'), - ('validated', 'Validated'), - ('no_eligible', 'No eligible'), - ('sent', 'Sent')], - string='State', required=True, default="draft") - declaration_id = fields.Many2one('tax.shelter.declaration', - string='Declaration', required=True, - readonly=True) - lines = fields.One2many('certificate.line', - 'tax_shelter_certificate', - string='Certificate lines', readonly=True) - previously_subscribed_lines = fields.One2many(compute='_compute_certificate_lines', - comodel_name='certificate.line', - string='Previously Subscribed lines', - readonly=True) - previously_subscribed_eligible_lines = fields.One2many( - compute='_compute_certificate_lines', - comodel_name='certificate.line', - string='Previously Subscribed eligible lines', - readonly=True) - subscribed_lines = fields.One2many(compute='_compute_certificate_lines', - comodel_name='certificate.line', - string='Shares subscribed', - readonly=True) - resold_lines = fields.One2many(compute='_compute_certificate_lines', - comodel_name='certificate.line', - string='Shares resold', - readonly=True) - transfered_lines = fields.One2many(compute='_compute_certificate_lines', - comodel_name='certificate.line', - string='Shares transfered', - readonly=True) - total_amount_previously_subscribed = fields.Float(compute='_compute_amounts', - string='Total previously subscribed') - total_amount_eligible_previously_subscribed = fields.Float(compute='_compute_amounts', - string='Total eligible previously subscribed') - total_amount_subscribed = fields.Float(compute='_compute_amounts', - string='Total subscribed') - total_amount_eligible = fields.Float(compute='_compute_amounts', - string='Total amount eligible To Tax shelter') - total_amount_resold = fields.Float(compute='_compute_amounts', - string='Total resold') - total_amount_transfered = fields.Float(compute='_compute_amounts', - string='Total transfered') - total_amount = fields.Float(compute='_compute_amounts', - string='Total', readonly=True) - company_id = fields.Many2one(related="declaration_id.company_id", - string="Company") - - def generate_pdf_report(self, report_type): - report_action, name = REPORT_DIC[report_type] - report = self.env['report'].get_pdf(self, report_action) - report = base64.b64encode(report) - report_name = self.partner_id.name + ' ' + name + ' ' + self.declaration_id.name + '.pdf' - - return (report_name, report) - - def generate_certificates_report(self): - attachments = [] - if self.total_amount_eligible > 0: - attachments.append(self.generate_pdf_report('subscription')) - if self.partner_id.total_value > 0: - attachments.append(self.generate_pdf_report('shares')) - # if self.total_amount_resold > 0 or self.total_amount_transfered > 0: - # TODO - return attachments - - @api.multi - def send_certificates(self): - tax_shelter_mail_template = self.env.ref('easy_my_coop_taxshelter_report.email_template_tax_shelter_certificate', False) - for certificate in self: - if certificate.total_amount_eligible + certificate.total_amount_eligible_previously_subscribed > 0: - attachments = certificate.generate_certificates_report() - if len(attachments) > 0: - tax_shelter_mail_template.send_mail_with_multiple_attachments(certificate.id, attachments,True) - certificate.state = 'sent' - else: - certificate.state = 'no_eligible' - self.env.cr.commit() - - @api.multi - def print_subscription_certificate(self): - self.ensure_one() - return self.env['report'].get_action(self, 'easy_my_coop_taxshelter_report.tax_shelter_subscription_report') - - @api.multi - def print_shares_certificate(self): - self.ensure_one() - return self.env['report'].get_action(self, 'easy_my_coop_taxshelter_report.tax_shelter_shares_report') - - @api.multi - def _compute_amounts(self): - for certificate in self: - total_amount_previously_subscribed = 0 - total_amount_previously_eligible = 0 - total_amount_subscribed = 0 - total_amount_elligible = 0 - total_amount_transfered = 0 - total_amount_resold = 0 - - for line in certificate.subscribed_lines: - total_amount_subscribed += line.amount_subscribed - total_amount_elligible += line.amount_subscribed_eligible - certificate.total_amount_subscribed = total_amount_subscribed - certificate.total_amount_eligible = total_amount_elligible - - for line in certificate.previously_subscribed_eligible_lines: - total_amount_previously_eligible += line.amount_subscribed_eligible - certificate.total_amount_eligible_previously_subscribed = total_amount_previously_eligible - - for line in certificate.previously_subscribed_lines: - total_amount_previously_subscribed += line.amount_subscribed - certificate.total_amount_previously_subscribed = total_amount_previously_subscribed - - for line in certificate.transfered_lines: - total_amount_transfered += line.amount_transfered - certificate.total_amount_transfered = total_amount_transfered - - for line in certificate.resold_lines: - total_amount_resold += line.amount_resold - certificate.total_amount_resold = total_amount_resold - certificate.total_amount = certificate.total_amount_previously_subscribed + certificate.total_amount_subscribed + certificate.total_amount_resold + certificate.total_amount_transfered - - @api.multi - def _compute_certificate_lines(self): - for certificate in self: - certificate.previously_subscribed_lines = certificate.lines.filtered(lambda r: r.type == 'subscribed' and r.transaction_date < certificate.declaration_id.date_from) - certificate.previously_subscribed_eligible_lines = certificate.lines.filtered(lambda r: r.type == 'subscribed' and r.transaction_date < certificate.declaration_id.date_from and r.tax_shelter) - certificate.subscribed_lines = certificate.lines.filtered(lambda r: r.type == 'subscribed' and r.transaction_date >= certificate.declaration_id.date_from and r.transaction_date <= certificate.declaration_id.date_to) - certificate.resold_lines = certificate.lines.filtered(lambda r: r.type == 'resold' and r.transaction_date >= certificate.declaration_id.date_from and r.transaction_date <= certificate.declaration_id.date_to) - certificate.transfered_lines = certificate.lines.filtered(lambda r: r.type == 'transfered' and r.transaction_date >= certificate.declaration_id.date_from and r.transaction_date <= certificate.declaration_id.date_to) - - @api.model - def batch_send_tax_shelter_certificate(self): - certificates = self.search([('state', '=', 'validated')], limit=80) - certificates.send_certificates() - - -class TaxShelterCertificateLine(models.Model): - - _name = "certificate.line" - - declaration_id = fields.Many2one(related='tax_shelter_certificate.declaration_id', - string="Declaration") - tax_shelter_certificate = fields.Many2one('tax.shelter.certificate', - string="Tax shelter certificate", - ondelete='cascade', - required=True) - share_type = fields.Many2one('product.product', - string='Share type', - required=True, - readonly=True) - share_unit_price = fields.Float(string='Share price', - required=True, - readonly=True) - quantity = fields.Integer(string='Number of shares', - required=True, - readonly=True) - transaction_date = fields.Date(string="Transaction date") - tax_shelter = fields.Boolean(string="Tax shelter eligible", readonly=True) - type = fields.Selection([('subscribed', 'Subscribed'), - ('resold', 'Resold'), - ('transfered', 'Transfered'), - ('kept', 'Kept')], - required=True, readonly=True) - amount_subscribed = fields.Float(compute='_compute_totals', - string='Amount subscribed', - store=True) - amount_subscribed_eligible = fields.Float(compute='_compute_totals', - string='Amount subscribed eligible', - store=True) - amount_resold = fields.Float(compute='_compute_totals', - string='Amount resold', - store=True) - amount_transfered = fields.Float(compute='_compute_totals', - string='Amount transfered', - store=True) - share_short_name = fields.Char(string='Share type name', readonly=True) - capital_before_sub = fields.Float(string="Capital before subscription", - readonly=True) - capital_after_sub = fields.Float(string="Capital after subscription", - readonly=True) - capital_limit = fields.Float(string="Capital limit", readonly=True) - - @api.multi - @api.depends('quantity', 'share_unit_price') - def _compute_totals(self): - for line in self: - if line.type == 'subscribed': - line.amount_subscribed = line.share_unit_price * line.quantity - if line.type == 'subscribed' and line.tax_shelter: - if (line.capital_before_sub < line.capital_limit - and line.capital_after_sub >= line.capital_limit): - line.amount_subscribed_eligible = line.capital_limit - line.capital_before_sub #noqa - elif (line.capital_before_sub < line.capital_limit - and line.capital_after_sub <= line.capital_limit): - line.amount_subscribed_eligible = line.share_unit_price * line.quantity #noqa - elif line.capital_before_sub >= line.capital_limit: - line.amount_subscribed_eligible = 0 - if line.type == 'resold': - line.amount_resold = line.share_unit_price * -(line.quantity) - if line.type == 'transfered': - line.amount_transfered = line.share_unit_price * -(line.quantity) #noqa +import base64 + +from odoo import api, fields, models + +TYPE_MAP = { + "subscription": "subscribed", + "transfer": "transfered", + "sell_back": "resold", +} + +REPORT_DIC = { + "subscription": ( + "easy_my_coop_taxshelter_report.action_tax_shelter_subscription_report", + "Tax Shelter Subscription", + ), + "shares": ( + "easy_my_coop_taxshelter_report.action_tax_shelter_shares_report", + "Tax Shelter Shares", + ), +} + + +class TaxShelterDeclaration(models.Model): + + _name = "tax.shelter.declaration" + _description = "Tax Shelter Declaration" + + name = fields.Char(string="Declaration year", required=True) + fiscal_year = fields.Char(String="Fiscal year", required=True) + tax_shelter_certificates = fields.One2many( + "tax.shelter.certificate", + "declaration_id", + string="Tax shelter certificates", + readonly=True, + ) + date_from = fields.Date(string="Date from", required=True) + date_to = fields.Date(string="Date to", required=True) + month_from = fields.Char(String="Month from", required=True) + month_to = fields.Char(String="Month to", required=True) + tax_shelter_percentage = fields.Selection( + [("30", "30%"), ("45", "45%")], string="Tax Shelter percentage", required=True + ) + state = fields.Selection( + [("draft", "Draft"), ("computed", "Computed"), ("validated", "Validated")], + string="State", + required=True, + default="draft", + ) + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + change_default=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) + tax_shelter_capital_limit = fields.Float( + string="Tax shelter capital limit", required=True + ) + previously_subscribed_capital = fields.Float( + String="Capital previously subscribed", readonly=True + ) + excluded_cooperator = fields.Many2many( + "res.partner", + string="Excluded cooperator", + domain=[("cooperator", "=", True)], + help="If these cooperator have" + " subscribed share during the time" + " frame of this Tax Shelter " + "Declaration. They will be marked " + "as non eligible", + ) + + def _excluded_from_declaration(self, entry): + if entry.date >= self.date_from and entry.date <= self.date_to: + declaration = self + else: + declaration = self.search( + [("date_from", "<=", entry.date), ("date_to", ">=", entry.date)] + ) + if entry.partner_id.id in declaration.excluded_cooperator.ids: + return True + return False + + def _prepare_line(self, certificate, entry, ongoing_capital_sub, excluded): + line_vals = {} + line_vals["tax_shelter_certificate"] = certificate.id + line_vals["share_type"] = entry.share_product_id.id + line_vals["share_short_name"] = entry.share_short_name + line_vals["share_unit_price"] = entry.share_unit_price + line_vals["quantity"] = entry.quantity + line_vals["transaction_date"] = entry.date + line_vals["type"] = TYPE_MAP[entry.type] + if entry.type == "subscription": + if not excluded: + capital_after_sub = ongoing_capital_sub + entry.total_amount_line + else: + capital_after_sub = ongoing_capital_sub + line_vals["capital_before_sub"] = ongoing_capital_sub + line_vals["capital_after_sub"] = capital_after_sub + line_vals["capital_limit"] = self.tax_shelter_capital_limit + if ongoing_capital_sub < self.tax_shelter_capital_limit and not excluded: + line_vals["tax_shelter"] = True + return line_vals + + def _compute_certificates(self, entries, partner_certificate): + ongoing_capital_sub = 0.0 + for entry in entries: + certificate = partner_certificate.get(entry.partner_id.id, False) + + if not certificate: + # create a certificate for this cooperator + cert_vals = {} + cert_vals["declaration_id"] = self.id + cert_vals["partner_id"] = entry.partner_id.id + cert_vals[ + "cooperator_number" + ] = entry.partner_id.cooperator_register_number + certificate = self.env["tax.shelter.certificate"].create(cert_vals) + partner_certificate[entry.partner_id.id] = certificate + excluded = self._excluded_from_declaration(entry) + line_vals = self._prepare_line( + certificate, entry, ongoing_capital_sub, excluded + ) + certificate.write({"lines": [(0, 0, line_vals)]}) + + if entry.type == "subscription" and not excluded: + ongoing_capital_sub += entry.total_amount_line + + return partner_certificate + + @api.one + def compute_declaration(self): + entries = self.env["subscription.register"].search( + [ + ("partner_id.is_company", "=", False), + ("date", "<=", self.date_to), + ("type", "in", ["subscription", "sell_back", "transfer"]), + ] + ) + + subscriptions = entries.filtered( + (lambda r: r.type == "subscription" and r.date < self.date_from) + ) # noqa + cap_prev_sub = 0.0 + for subscription in subscriptions: + cap_prev_sub += subscription.total_amount_line + + self.previously_subscribed_capital = cap_prev_sub + + partner_cert = {} + + partner_cert = self._compute_certificates(entries, partner_cert) + + self.state = "computed" + + @api.one + def validate_declaration(self): + self.tax_shelter_certificates.write({"state": "validated"}) + self.state = "validated" + + @api.one + def reset_declaration(self): + if not self.state == "validated": + self.tax_shelter_certificates.unlink() + self.state = "draft" + + +class TaxShelterCertificate(models.Model): + _name = "tax.shelter.certificate" + _description = "Tax Shelter Certificate" + _order = "cooperator_number asc" + + cooperator_number = fields.Integer( + string="Cooperator number", required=True, readonly=True + ) + partner_id = fields.Many2one( + "res.partner", string="Cooperator", required=True, readonly=True + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("validated", "Validated"), + ("no_eligible", "No eligible"), + ("sent", "Sent"), + ], + string="State", + required=True, + default="draft", + ) + declaration_id = fields.Many2one( + "tax.shelter.declaration", + string="Declaration", + required=True, + readonly=True, + ondelete="restrict", + ) + lines = fields.One2many( + "certificate.line", + "tax_shelter_certificate", + string="Certificate lines", + readonly=True, + ) + previously_subscribed_lines = fields.One2many( + compute="_compute_certificate_lines", + comodel_name="certificate.line", + string="Previously Subscribed lines", + readonly=True, + ) + previously_subscribed_eligible_lines = fields.One2many( + compute="_compute_certificate_lines", + comodel_name="certificate.line", + string="Previously Subscribed eligible lines", + readonly=True, + ) + subscribed_lines = fields.One2many( + compute="_compute_certificate_lines", + comodel_name="certificate.line", + string="Shares subscribed", + readonly=True, + ) + resold_lines = fields.One2many( + compute="_compute_certificate_lines", + comodel_name="certificate.line", + string="Shares resold", + readonly=True, + ) + transfered_lines = fields.One2many( + compute="_compute_certificate_lines", + comodel_name="certificate.line", + string="Shares transfered", + readonly=True, + ) + total_amount_previously_subscribed = fields.Float( + compute="_compute_amounts", string="Total previously subscribed" + ) + total_amount_eligible_previously_subscribed = fields.Float( + compute="_compute_amounts", string="Total eligible previously subscribed" + ) + total_amount_subscribed = fields.Float( + compute="_compute_amounts", string="Total subscribed" + ) + total_amount_eligible = fields.Float( + compute="_compute_amounts", string="Total amount eligible To Tax shelter" + ) + total_amount_resold = fields.Float( + compute="_compute_amounts", string="Total resold" + ) + total_amount_transfered = fields.Float( + compute="_compute_amounts", string="Total transfered" + ) + total_amount = fields.Float( + compute="_compute_amounts", string="Total", readonly=True + ) + company_id = fields.Many2one(related="declaration_id.company_id", string="Company") + + def generate_pdf_report(self, report_type): + report, name = REPORT_DIC[report_type] + report = self.env.ref(report).render_qweb_pdf(self) + report = base64.b64encode(report) + report_name = ( + self.partner_id.name + " " + name + " " + self.declaration_id.name + ".pdf" + ) + + return (report_name, report) + + def generate_certificates_report(self): + attachments = [] + if self.total_amount_eligible > 0: + attachments.append(self.generate_pdf_report("subscription")) + if self.partner_id.total_value > 0: + attachments.append(self.generate_pdf_report("shares")) + # if self.total_amount_resold > 0 or self.total_amount_transfered > 0: + # TODO + return attachments + + @api.multi + def send_certificates(self): + tax_shelter_mail_template = self.env.ref( + "easy_my_coop_taxshelter_report.email_template_tax_shelter_certificate", + False, + ) + for certificate in self: + if ( + certificate.total_amount_eligible + + certificate.total_amount_eligible_previously_subscribed + > 0 + ): + attachments = certificate.generate_certificates_report() + if len(attachments) > 0: + tax_shelter_mail_template.send_mail_with_multiple_attachments( + certificate.id, attachments, True + ) + certificate.state = "sent" + else: + certificate.state = "no_eligible" + self.env.cr.commit() + + @api.multi + def print_subscription_certificate(self): + self.ensure_one() + report, name = REPORT_DIC["subscription"] + return self.env.ref(report).report_action(self) + + @api.multi + def print_shares_certificate(self): + self.ensure_one() + report, name = REPORT_DIC["shares"] + return self.env.ref(report).report_action(self) + + @api.multi + def _compute_amounts(self): + for certificate in self: + total_amount_previously_subscribed = 0 + total_amount_previously_eligible = 0 + total_amount_subscribed = 0 + total_amount_elligible = 0 + total_amount_transfered = 0 + total_amount_resold = 0 + + for line in certificate.subscribed_lines: + total_amount_subscribed += line.amount_subscribed + total_amount_elligible += line.amount_subscribed_eligible + certificate.total_amount_subscribed = total_amount_subscribed + certificate.total_amount_eligible = total_amount_elligible + + for line in certificate.previously_subscribed_eligible_lines: + total_amount_previously_eligible += line.amount_subscribed_eligible + certificate.total_amount_eligible_previously_subscribed = ( + total_amount_previously_eligible + ) + + for line in certificate.previously_subscribed_lines: + total_amount_previously_subscribed += line.amount_subscribed + certificate.total_amount_previously_subscribed = ( + total_amount_previously_subscribed + ) + + for line in certificate.transfered_lines: + total_amount_transfered += line.amount_transfered + certificate.total_amount_transfered = total_amount_transfered + + for line in certificate.resold_lines: + total_amount_resold += line.amount_resold + certificate.total_amount_resold = total_amount_resold + certificate.total_amount = ( + certificate.total_amount_previously_subscribed + + certificate.total_amount_subscribed + + certificate.total_amount_resold + + certificate.total_amount_transfered + ) + + @api.depends("lines") + def _compute_certificate_lines(self): + for certificate in self: + certificate.previously_subscribed_lines = certificate.lines.filtered( + lambda r: r.type == "subscribed" + and r.transaction_date < certificate.declaration_id.date_from + ) + certificate.previously_subscribed_eligible_lines = certificate.lines.filtered( + lambda r: r.type == "subscribed" + and r.transaction_date < certificate.declaration_id.date_from + and r.tax_shelter + ) + certificate.subscribed_lines = certificate.lines.filtered( + lambda r: r.type == "subscribed" + and r.transaction_date >= certificate.declaration_id.date_from + and r.transaction_date <= certificate.declaration_id.date_to + ) + certificate.resold_lines = certificate.lines.filtered( + lambda r: r.type == "resold" + and r.transaction_date >= certificate.declaration_id.date_from + and r.transaction_date <= certificate.declaration_id.date_to + ) + certificate.transfered_lines = certificate.lines.filtered( + lambda r: r.type == "transfered" + and r.transaction_date >= certificate.declaration_id.date_from + and r.transaction_date <= certificate.declaration_id.date_to + ) + + @api.model + def batch_send_tax_shelter_certificate(self): + certificates = self.search([("state", "=", "validated")], limit=80) + certificates.send_certificates() + + +class TaxShelterCertificateLine(models.Model): + + _name = "certificate.line" + _description = "Tax Shelter Certificate Line" + + declaration_id = fields.Many2one( + related="tax_shelter_certificate.declaration_id", string="Declaration" + ) + tax_shelter_certificate = fields.Many2one( + "tax.shelter.certificate", + string="Tax shelter certificate", + ondelete="cascade", + required=True, + ) + share_type = fields.Many2one( + "product.product", string="Share type", required=True, readonly=True + ) + share_unit_price = fields.Float(string="Share price", required=True, readonly=True) + quantity = fields.Integer(string="Number of shares", required=True, readonly=True) + transaction_date = fields.Date(string="Transaction date") + tax_shelter = fields.Boolean(string="Tax shelter eligible", readonly=True) + type = fields.Selection( + [ + ("subscribed", "Subscribed"), + ("resold", "Resold"), + ("transfered", "Transfered"), + ("kept", "Kept"), + ], + required=True, + readonly=True, + ) + amount_subscribed = fields.Float( + compute="_compute_totals", string="Amount subscribed", store=True + ) + amount_subscribed_eligible = fields.Float( + compute="_compute_totals", string="Amount subscribed eligible", store=True + ) + amount_resold = fields.Float( + compute="_compute_totals", string="Amount resold", store=True + ) + amount_transfered = fields.Float( + compute="_compute_totals", string="Amount transfered", store=True + ) + share_short_name = fields.Char(string="Share type name", readonly=True) + capital_before_sub = fields.Float( + string="Capital before subscription", readonly=True + ) + capital_after_sub = fields.Float(string="Capital after subscription", readonly=True) + capital_limit = fields.Float(string="Capital limit", readonly=True) + + @api.multi + @api.depends("quantity", "share_unit_price") + def _compute_totals(self): + for line in self: + if line.type == "subscribed": + line.amount_subscribed = line.share_unit_price * line.quantity + if line.type == "subscribed" and line.tax_shelter: + if ( + line.capital_before_sub < line.capital_limit + and line.capital_after_sub >= line.capital_limit + ): + line.amount_subscribed_eligible = ( + line.capital_limit - line.capital_before_sub + ) + elif ( + line.capital_before_sub < line.capital_limit + and line.capital_after_sub <= line.capital_limit + ): + line.amount_subscribed_eligible = ( + line.share_unit_price * line.quantity + ) + elif line.capital_before_sub >= line.capital_limit: + line.amount_subscribed_eligible = 0 + if line.type == "resold": + line.amount_resold = line.share_unit_price * -(line.quantity) + if line.type == "transfered": + line.amount_transfered = line.share_unit_price * -(line.quantity) diff --git a/easy_my_coop_taxshelter_report/reports/tax_shelter_report.xml b/easy_my_coop_taxshelter_report/reports/tax_shelter_report.xml index dc398ca..1d9b346 100644 --- a/easy_my_coop_taxshelter_report/reports/tax_shelter_report.xml +++ b/easy_my_coop_taxshelter_report/reports/tax_shelter_report.xml @@ -1,24 +1,23 @@ - - - - - - - - + + + + + + + diff --git a/easy_my_coop_taxshelter_report/reports/tax_shelter_resold_report.xml b/easy_my_coop_taxshelter_report/reports/tax_shelter_resold_report.xml index e5abd65..0c21bb6 100644 --- a/easy_my_coop_taxshelter_report/reports/tax_shelter_resold_report.xml +++ b/easy_my_coop_taxshelter_report/reports/tax_shelter_resold_report.xml @@ -1,127 +1,81 @@ - - - - - - - - + + + + + + diff --git a/easy_my_coop_taxshelter_report/reports/tax_shelter_shares_report.xml b/easy_my_coop_taxshelter_report/reports/tax_shelter_shares_report.xml index 21f311c..2af3be7 100644 --- a/easy_my_coop_taxshelter_report/reports/tax_shelter_shares_report.xml +++ b/easy_my_coop_taxshelter_report/reports/tax_shelter_shares_report.xml @@ -1,177 +1,133 @@ - - - - - - - - + + + + + + diff --git a/easy_my_coop_taxshelter_report/reports/tax_shelter_subscription_report.xml b/easy_my_coop_taxshelter_report/reports/tax_shelter_subscription_report.xml index 4cec67e..1bb689c 100644 --- a/easy_my_coop_taxshelter_report/reports/tax_shelter_subscription_report.xml +++ b/easy_my_coop_taxshelter_report/reports/tax_shelter_subscription_report.xml @@ -1,158 +1,110 @@ - - - - - - - - + + + + + + diff --git a/easy_my_coop_taxshelter_report/security/ir.model.access.csv b/easy_my_coop_taxshelter_report/security/ir.model.access.csv index 92e0a57..ff996c8 100644 --- a/easy_my_coop_taxshelter_report/security/ir.model.access.csv +++ b/easy_my_coop_taxshelter_report/security/ir.model.access.csv @@ -1,7 +1,7 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_tax_shelter_declaration,tax.shelter.declaration,model_tax_shelter_declaration,easy_my_coop.group_energiris_user,1,0,0,0 -access_tax_shelter_declaration,tax.shelter.declaration,model_tax_shelter_declaration,easy_my_coop.group_energiris_manager,1,1,1,1 -access_tax_shelter_certificate,tax.shelter.certificate,model_tax_shelter_certificate,easy_my_coop.group_energiris_user,1,0,0,0 -access_tax_shelter_certificate,tax.shelter.certificate,model_tax_shelter_certificate,easy_my_coop.group_energiris_manager,1,1,1,1 -access_certificate_line,certificate.line,model_certificate_line,easy_my_coop.group_energiris_user,1,0,0,0 -access_certificate_line,certificate.line,model_certificate_line,easy_my_coop.group_energiris_manager,1,1,1,1 \ No newline at end of file +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_tax_shelter_declaration,tax.shelter.declaration,model_tax_shelter_declaration,easy_my_coop.group_easy_my_coop_user,1,0,0,0 +access_tax_shelter_declaration,tax.shelter.declaration,model_tax_shelter_declaration,easy_my_coop.group_easy_my_coop_manager,1,1,1,1 +access_tax_shelter_certificate,tax.shelter.certificate,model_tax_shelter_certificate,easy_my_coop.group_easy_my_coop_user,1,0,0,0 +access_tax_shelter_certificate,tax.shelter.certificate,model_tax_shelter_certificate,easy_my_coop.group_easy_my_coop_manager,1,1,1,1 +access_certificate_line,certificate.line,model_certificate_line,easy_my_coop.group_easy_my_coop_user,1,0,0,0 +access_certificate_line,certificate.line,model_certificate_line,easy_my_coop.group_easy_my_coop_manager,1,1,1,1 diff --git a/easy_my_coop_taxshelter_report/static/src/css/coop_report.css b/easy_my_coop_taxshelter_report/static/src/css/coop_report.css deleted file mode 100644 index 08d8838..0000000 --- a/easy_my_coop_taxshelter_report/static/src/css/coop_report.css +++ /dev/null @@ -1,19 +0,0 @@ -.border-top-easymy-coop td { - border-top: 1pt solid #e7511e !important; -} - -.border-easymy-coop th { - border-bottom: 1pt solid #e7511e !important; -} - -.easymy-coop-normal { - font-family:Roboto-Bold; - font-size:12px; - line-height:14pt; -} - -.easymy-coop-info-title { - font-family:Roboto-Bold; - font-size:12px; - line-height:14pt; -} \ No newline at end of file diff --git a/easy_my_coop_taxshelter_report/views/tax_shelter_declaration_view.xml b/easy_my_coop_taxshelter_report/views/tax_shelter_declaration_view.xml index ae347db..4fd86a3 100644 --- a/easy_my_coop_taxshelter_report/views/tax_shelter_declaration_view.xml +++ b/easy_my_coop_taxshelter_report/views/tax_shelter_declaration_view.xml @@ -1,201 +1,199 @@ - - - - - - - tax.shelter.declaration.form - tax.shelter.declaration - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -