From b05064d3127efde0c15e4a94ca5e2a58357a47ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Taymans?= Date: Wed, 26 Feb 2020 15:17:23 +0100 Subject: [PATCH] [FIX] emc_taxshelter_report: Line ending --- .../__manifest__.py | 2 +- .../data/mail_template_data.xml | 113 +-- .../data/scheduler_data.xml | 30 +- .../models/__init__.py | 2 +- .../models/mail_template.py | 115 ++- .../models/tax_shelter_declaration.py | 738 +++++++++--------- .../reports/tax_shelter_report.xml | 47 +- .../reports/tax_shelter_resold_report.xml | 162 ++-- .../reports/tax_shelter_shares_report.xml | 266 +++---- .../tax_shelter_subscription_report.xml | 220 +++--- .../security/ir.model.access.csv | 14 +- .../static/src/css/coop_report.css | 36 +- .../views/tax_shelter_declaration_view.xml | 400 +++++----- 13 files changed, 1072 insertions(+), 1073 deletions(-) diff --git a/easy_my_coop_taxshelter_report/__manifest__.py b/easy_my_coop_taxshelter_report/__manifest__.py index dbdf468..f773c08 100644 --- a/easy_my_coop_taxshelter_report/__manifest__.py +++ b/easy_my_coop_taxshelter_report/__manifest__.py @@ -21,7 +21,7 @@ { # todo check ir model access "name": "Easy My Coop tax shelter report", - "version": "12.0.1.0.0", + "version": "12.0.1.0.1", "depends": ["easy_my_coop"], "author": "Houssine BAKKALI ", "category": "Cooperative management", 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 99eccfd..109f186 100644 --- a/easy_my_coop_taxshelter_report/data/scheduler_data.xml +++ b/easy_my_coop_taxshelter_report/data/scheduler_data.xml @@ -1,15 +1,15 @@ - - - - - Tax shelter mail batch mail - - code - model.batch_send_tax_shelter_certificate() - 1 - hours - -1 - - - - + + + + + Tax shelter mail batch mail + + code + model.batch_send_tax_shelter_certificate() + 1 + hours + -1 + + + + diff --git a/easy_my_coop_taxshelter_report/models/__init__.py b/easy_my_coop_taxshelter_report/models/__init__.py index ab4f736..2582327 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 tax_shelter_declaration from . import mail_template \ No newline at end of file diff --git a/easy_my_coop_taxshelter_report/models/mail_template.py b/easy_my_coop_taxshelter_report/models/mail_template.py index 4d44e0a..cc05ca8 100644 --- a/easy_my_coop_taxshelter_report/models/mail_template.py +++ b/easy_my_coop_taxshelter_report/models/mail_template.py @@ -1,60 +1,55 @@ -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 e2800f0..4c2dd0e 100644 --- a/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py +++ b/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py @@ -1,366 +1,372 @@ -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 #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 d0d8365..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,81 +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 00eafbd..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,133 +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 e43c367..7f3931e 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,110 +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 f17ad41..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_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 +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 index 08d8838..b9d03d8 100644 --- a/easy_my_coop_taxshelter_report/static/src/css/coop_report.css +++ b/easy_my_coop_taxshelter_report/static/src/css/coop_report.css @@ -1,19 +1,19 @@ -.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; +.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 - -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - -