diff --git a/.isort.cfg b/.isort.cfg index ed8c868..821e287 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -9,4 +9,4 @@ line_length=79 known_odoo=odoo known_odoo_addons=odoo.addons sections=FUTURE,STDLIB,THIRDPARTY,ODOO,ODOO_ADDONS,FIRSTPARTY,LOCALFOLDER -known_third_party=dateutil,phonenumbers +known_third_party=addons,cStringIO,openerp,requests,werkzeug,xlsxwriter diff --git a/README.md b/README.md index e19d749..51bff9e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,44 @@ # vertical-cooperative This repository gather odoo modules for cooperatives + +# MAKE TRAVIS GREEN AGAIN + +pre-commit still issues these messages. They need to be fixed. + +``` +************* Module easy_my_coop.models.partner +easy_my_coop/models/partner.py:56: [E8103(sql-injection), ResPartner._invoice_total] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module partner_age.models.partner +partner_age/models/partner.py:13: [E8103(sql-injection), ResPartner._search_age] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module easy_my_coop_taxshelter_report.models.tax_shelter_declaration +easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py:325: [E8102(invalid-commit), TaxShelterCertificate.send_certificates] Use of cr.commit() directly - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#never-commit-the-transaction +************* Module easy_my_coop.models.account_invoice +easy_my_coop/models/account_invoice.py:11: [C8104(class-camelcase), account_invoice] Use `CamelCase` "AccountInvoice" in class name "account_invoice". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.operation_request +easy_my_coop/models/operation_request.py:12: [C8104(class-camelcase), operation_request] Use `CamelCase` "OperationRequest" in class name "operation_request". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.coop +easy_my_coop/models/coop.py:287: [C8108(method-compute), SubscriptionRequest] Name of compute method should start with "_compute_" +************* Module website_recaptcha_reloaded.models.res_config +website_recaptcha_reloaded/models/res_config.py:7: [C8104(class-camelcase), website_config_settings] Use `CamelCase` "WebsiteConfigSettings" in class name "website_config_settings". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.company +easy_my_coop/models/company.py:61: [C8108(method-compute), ResCompany] Name of compute method should start with "_compute_" + +pylint with mandatory checks.............................................Failed +- hook id: pylint +- exit code: 18 + +************* Module easy_my_coop.models.partner +easy_my_coop/models/partner.py:56: [E8103(sql-injection), ResPartner._invoice_total] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module partner_age.models.partner +partner_age/models/partner.py:13: [E8103(sql-injection), ResPartner._search_age] SQL injection risk. Use parameters if you can. - More info https://github.com/OCA/odoo-community.org/blob/master/website/Contribution/CONTRIBUTING.rst#no-sql-injection +************* Module easy_my_coop.models.account_invoice +easy_my_coop/models/account_invoice.py:11: [C8104(class-camelcase), account_invoice] Use `CamelCase` "AccountInvoice" in class name "account_invoice". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.operation_request +easy_my_coop/models/operation_request.py:12: [C8104(class-camelcase), operation_request] Use `CamelCase` "OperationRequest" in class name "operation_request". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.coop +easy_my_coop/models/coop.py:287: [C8108(method-compute), SubscriptionRequest] Name of compute method should start with "_compute_" +************* Module website_recaptcha_reloaded.models.res_config +website_recaptcha_reloaded/models/res_config.py:7: [C8104(class-camelcase), website_config_settings] Use `CamelCase` "WebsiteConfigSettings" in class name "website_config_settings". You can use oca-autopep8 of https://github.com/OCA/maintainer-tools to auto fix it. +************* Module easy_my_coop.models.company +easy_my_coop/models/company.py:61: [C8108(method-compute), ResCompany] Name of compute method should start with "_compute_" +``` diff --git a/easy_my_coop/__manifest__.py b/easy_my_coop/__manifest__.py index 89b3073..a6bc9ae 100644 --- a/easy_my_coop/__manifest__.py +++ b/easy_my_coop/__manifest__.py @@ -4,59 +4,53 @@ # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - 'name': 'Easy My Coop', - 'version': '12.0.3.0.1', - 'depends': [ - 'base', - 'web', - 'sale', - 'account', - 'base_iban', - 'product', - 'partner_firstname', - 'partner_contact_birthdate', - 'partner_contact_address', - 'email_template_config', + "name": "Easy My Coop", + "summary": "Manage your cooperative shares", + "version": "12.0.3.0.1", + "depends": [ + "base", + "web", + "sale", + "account", + "base_iban", + "product", + "partner_firstname", + "partner_contact_birthdate", + "partner_contact_address", + "email_template_config", ], - 'author': 'Coop IT Easy SCRLfs', - 'category': 'Cooperative management', - 'website': 'https://www.coopiteasy.be', - 'license': 'AGPL-3', - 'description': """ - This module allows to manage the cooperator subscription and all the - cooperative business processes. - """, - 'data': [ - 'data/easy_my_coop_data.xml', - 'data/paperformat.xml', - 'security/res_groups.xml', - 'security/ir.model.access.csv', - 'wizard/create_subscription_from_partner.xml', - 'wizard/update_partner_info.xml', - 'wizard/validate_subscription_request.xml', - 'wizard/update_share_line.xml', - 'views/subscription_request_view.xml', - 'views/email_template_view.xml', - 'views/res_partner_view.xml', - 'views/cooperator_register_view.xml', - 'views/operation_request_view.xml', - 'views/account_invoice_view.xml', - 'views/product_view.xml', - 'views/res_company_view.xml', - 'views/account_journal_view.xml', - 'views/menus.xml', - 'report/easy_my_coop_report.xml', - 'report/layout.xml', - 'report/cooperator_invoice_G002.xml', - 'report/cooperator_certificat_G001.xml', - 'report/cooperator_subscription_G001.xml', - 'report/cooperator_register_G001.xml', - 'data/mail_template_data.xml', # Must be loaded after reports + "author": "Coop IT Easy SCRLfs", + "category": "Cooperative management", + "website": "https://www.coopiteasy.be", + "license": "AGPL-3", + "data": [ + "data/easy_my_coop_data.xml", + "data/paperformat.xml", + "security/res_groups.xml", + "security/ir.model.access.csv", + "wizard/create_subscription_from_partner.xml", + "wizard/update_partner_info.xml", + "wizard/validate_subscription_request.xml", + "wizard/update_share_line.xml", + "views/subscription_request_view.xml", + "views/email_template_view.xml", + "views/res_partner_view.xml", + "views/cooperator_register_view.xml", + "views/operation_request_view.xml", + "views/account_invoice_view.xml", + "views/product_view.xml", + "views/res_company_view.xml", + "views/account_journal_view.xml", + "views/menus.xml", + "report/easy_my_coop_report.xml", + "report/layout.xml", + "report/cooperator_invoice_G002.xml", + "report/cooperator_certificat_G001.xml", + "report/cooperator_subscription_G001.xml", + "report/cooperator_register_G001.xml", + "data/mail_template_data.xml", # Must be loaded after reports ], - 'demo': [ - 'demo/coop.xml', - 'demo/users.xml', - ], - 'installable': True, - 'application': True, + "demo": ["demo/coop.xml", "demo/users.xml"], + "installable": True, + "application": True, } diff --git a/easy_my_coop/migrations/8.0.1.0/pre-migration.py b/easy_my_coop/migrations/8.0.1.0/pre-migration.py index fc9f222..c0776dd 100644 --- a/easy_my_coop/migrations/8.0.1.0/pre-migration.py +++ b/easy_my_coop/migrations/8.0.1.0/pre-migration.py @@ -1,30 +1,29 @@ -# -*- coding: utf-8 -*- - -from openerp.openupgrade import openupgrade -import logging - -logger = logging.getLogger('OpenUpgrade') - -column_renames = { - 'job_sync_line': [ - ('adresse', 'address'), - ('ville', 'city'), - ('codepostal', 'zip'), - ('sync_date','date'), - ], - } - -tables_renames = [ - ('job_sync_line','subscription_request'), - ('job_sync',None), - ('external_db',None), -] - -@openupgrade.migrate() -def migrate(cr, version): - if not version: - return - - openupgrade.rename_columns(cr, column_renames) - openupgrade.rename_tables(cr, tables_renames) - +import logging + +from openerp.openupgrade import openupgrade + +logger = logging.getLogger("OpenUpgrade") + +column_renames = { + "job_sync_line": [ + ("adresse", "address"), + ("ville", "city"), + ("codepostal", "zip"), + ("sync_date", "date"), + ] +} + +tables_renames = [ + ("job_sync_line", "subscription_request"), + ("job_sync", None), + ("external_db", None), +] + + +@openupgrade.migrate() +def migrate(cr, version): + if not version: + return + + openupgrade.rename_columns(cr, column_renames) + openupgrade.rename_tables(cr, tables_renames) diff --git a/easy_my_coop/models/account_invoice.py b/easy_my_coop/models/account_invoice.py index 8b2382e..2961085 100644 --- a/easy_my_coop/models/account_invoice.py +++ b/easy_my_coop/models/account_invoice.py @@ -1,175 +1,197 @@ -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models - - -class account_invoice(models.Model): - _inherit = 'account.invoice' - - subscription_request = fields.Many2one('subscription.request', - string='Subscription request') - release_capital_request = fields.Boolean( - string='Release of capital request') - - @api.model - def _prepare_refund(self, invoice, date_invoice=None, date=None, - description=None, journal_id=None): - values = super(account_invoice, self)._prepare_refund( - invoice, date_invoice, date, - description, journal_id) - values['release_capital_request'] = self.release_capital_request - - return values - - def create_user(self, partner): - user_obj = self.env['res.users'] - email = partner.email - - user = user_obj.search([('login', '=', email)]) - if not user: - user = user_obj.search([('login', '=', email), - ('active', '=', False)]) - if user: - user.sudo().write({'active': True}) - else: - user_values = {'partner_id': partner.id, 'login': email} - user = user_obj.sudo()._signup_create_user(user_values) - user.sudo().with_context({'create_user': True}).action_reset_password() - - return user - - def get_mail_template_certificate(self): - if self.partner_id.member: - mail_template = 'easy_my_coop.email_template_certificat_increase' - else: - mail_template = 'easy_my_coop.email_template_certificat' - return self.env.ref(mail_template) - - def get_sequence_register(self): - return self.env.ref('easy_my_coop.sequence_subscription', False) - - def get_sequence_operation(self): - return self.env.ref('easy_my_coop.sequence_register_operation', False) - - def get_share_line_vals(self, line, effective_date): - return { - 'share_number': line.quantity, - 'share_product_id': line.product_id.id, - 'partner_id': self.partner_id.id, - 'share_unit_price': line.price_unit, - 'effective_date': effective_date - } - - def get_subscription_register_vals(self, line, effective_date): - return { - 'partner_id': self.partner_id.id, - 'quantity': line.quantity, - 'share_product_id': line.product_id.id, - 'share_unit_price': line.price_unit, - 'date': effective_date, - 'type': 'subscription' - } - - def get_membership_vals(self): - # flag the partner as an effective member - # if not yet cooperator we generate a cooperator number - vals = {} - if self.partner_id.member is False \ - and self.partner_id.old_member is False: - sequence_id = self.get_sequence_register() - sub_reg_num = sequence_id.next_by_id() - vals = {'member': True, 'old_member': False, - 'cooperator_register_number': int(sub_reg_num) - } - elif self.partner_id.old_member: - vals = {'member': True, 'old_member': False} - - return vals - - def set_membership(self): - vals = self.get_membership_vals() - self.partner_id.write(vals) - - return True - - def send_certificate_email(self, certificate_email_template, sub_reg_line): - # we send the email with the certificate in attachment - certificate_email_template.sudo().send_mail(self.partner_id.id, False) - - def set_cooperator_effective(self, effective_date): - sub_register_obj = self.env['subscription.register'] - share_line_obj = self.env['share.line'] - - certificate_email_template = self.get_mail_template_certificate() - - self.set_membership() - - sequence_operation = self.get_sequence_operation() - sub_reg_operation = sequence_operation.next_by_id() - - for line in self.invoice_line_ids: - sub_reg_vals = self.get_subscription_register_vals(line, - effective_date) - sub_reg_vals['name'] = sub_reg_operation - sub_reg_vals['register_number_operation'] = int(sub_reg_operation) - - sub_reg_line = sub_register_obj.create(sub_reg_vals) - - share_line_vals = self.get_share_line_vals(line, effective_date) - share_line_obj.create(share_line_vals) - - if line.product_id.mail_template: - certificate_email_template = line.product_id.mail_template - - self.send_certificate_email(certificate_email_template, sub_reg_line) - - if self.company_id.create_user: - self.create_user(self.partner_id) - - return True - - def post_process_confirm_paid(self, effective_date): - self.set_cooperator_effective(effective_date) - - return True - - def get_refund_domain(self, invoice): - return [ - ('type', '=', 'out_refund'), - ('origin', '=', invoice.move_name) - ] - - @api.multi - def action_invoice_paid(self): - super(account_invoice, self).action_invoice_paid() - for invoice in self: - # we check if there is an open refund for this invoice. in this - # case we don't run the process_subscription function as the - # invoice has been reconciled with a refund and not a payment. - domain = self.get_refund_domain(invoice) - refund = self.search(domain) - - if invoice.partner_id.cooperator \ - and invoice.release_capital_request \ - and invoice.type == 'out_invoice' and not refund: - # take the effective date from the payment. - # by default the confirmation date is the payment date - effective_date = datetime.now().strftime("%d/%m/%Y") - - if invoice.payment_move_line_ids: - move_line = invoice.payment_move_line_ids[0] - effective_date = move_line.date - - invoice.subscription_request.state = 'paid' - invoice.post_process_confirm_paid(effective_date) - # if there is a open refund we mark the subscription as cancelled - elif invoice.partner_id.cooperator \ - and invoice.release_capital_request \ - and invoice.type == 'out_invoice' and refund: - invoice.subscription_request.state = 'cancelled' - return True +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +from odoo import api, fields, models + + +class AccountInvoice(models.Model): + _inherit = "account.invoice" + + subscription_request = fields.Many2one( + "subscription.request", string="Subscription request" + ) + release_capital_request = fields.Boolean( + string="Release of capital request" + ) + + @api.model + def _prepare_refund( + self, + invoice, + date_invoice=None, + date=None, + description=None, + journal_id=None, + ): + values = super(AccountInvoice, self)._prepare_refund( + invoice, date_invoice, date, description, journal_id + ) + values["release_capital_request"] = self.release_capital_request + + return values + + def create_user(self, partner): + user_obj = self.env["res.users"] + email = partner.email + + user = user_obj.search([("login", "=", email)]) + if not user: + user = user_obj.search( + [("login", "=", email), ("active", "=", False)] + ) + if user: + user.sudo().write({"active": True}) + else: + user_values = {"partner_id": partner.id, "login": email} + user = user_obj.sudo()._signup_create_user(user_values) + user.sudo().with_context( + {"create_user": True} + ).action_reset_password() + + return user + + def get_mail_template_certificate(self): + if self.partner_id.member: + mail_template = "easy_my_coop.email_template_certificat_increase" + else: + mail_template = "easy_my_coop.email_template_certificat" + return self.env.ref(mail_template) + + def get_sequence_register(self): + return self.env.ref("easy_my_coop.sequence_subscription", False) + + def get_sequence_operation(self): + return self.env.ref("easy_my_coop.sequence_register_operation", False) + + def get_share_line_vals(self, line, effective_date): + return { + "share_number": line.quantity, + "share_product_id": line.product_id.id, + "partner_id": self.partner_id.id, + "share_unit_price": line.price_unit, + "effective_date": effective_date, + } + + def get_subscription_register_vals(self, line, effective_date): + return { + "partner_id": self.partner_id.id, + "quantity": line.quantity, + "share_product_id": line.product_id.id, + "share_unit_price": line.price_unit, + "date": effective_date, + "type": "subscription", + } + + def get_membership_vals(self): + # flag the partner as an effective member + # if not yet cooperator we generate a cooperator number + vals = {} + if ( + self.partner_id.member is False + and self.partner_id.old_member is False + ): + sequence_id = self.get_sequence_register() + sub_reg_num = sequence_id.next_by_id() + vals = { + "member": True, + "old_member": False, + "cooperator_register_number": int(sub_reg_num), + } + elif self.partner_id.old_member: + vals = {"member": True, "old_member": False} + + return vals + + def set_membership(self): + vals = self.get_membership_vals() + self.partner_id.write(vals) + + return True + + def send_certificate_email(self, certificate_email_template, sub_reg_line): + # we send the email with the certificate in attachment + certificate_email_template.sudo().send_mail(self.partner_id.id, False) + + def set_cooperator_effective(self, effective_date): + sub_register_obj = self.env["subscription.register"] + share_line_obj = self.env["share.line"] + + certificate_email_template = self.get_mail_template_certificate() + + self.set_membership() + + sequence_operation = self.get_sequence_operation() + sub_reg_operation = sequence_operation.next_by_id() + + for line in self.invoice_line_ids: + sub_reg_vals = self.get_subscription_register_vals( + line, effective_date + ) + sub_reg_vals["name"] = sub_reg_operation + sub_reg_vals["register_number_operation"] = int(sub_reg_operation) + + sub_reg_line = sub_register_obj.create(sub_reg_vals) + + share_line_vals = self.get_share_line_vals(line, effective_date) + share_line_obj.create(share_line_vals) + + if line.product_id.mail_template: + certificate_email_template = line.product_id.mail_template + + self.send_certificate_email(certificate_email_template, sub_reg_line) + + if self.company_id.create_user: + self.create_user(self.partner_id) + + return True + + def post_process_confirm_paid(self, effective_date): + self.set_cooperator_effective(effective_date) + + return True + + def get_refund_domain(self, invoice): + return [ + ("type", "=", "out_refund"), + ("origin", "=", invoice.move_name), + ] + + @api.multi + def action_invoice_paid(self): + super(AccountInvoice, self).action_invoice_paid() + for invoice in self: + # we check if there is an open refund for this invoice. in this + # case we don't run the process_subscription function as the + # invoice has been reconciled with a refund and not a payment. + domain = self.get_refund_domain(invoice) + refund = self.search(domain) + + if ( + invoice.partner_id.cooperator + and invoice.release_capital_request + and invoice.type == "out_invoice" + and not refund + ): + # take the effective date from the payment. + # by default the confirmation date is the payment date + effective_date = datetime.now().strftime("%d/%m/%Y") + + if invoice.payment_move_line_ids: + move_line = invoice.payment_move_line_ids[0] + effective_date = move_line.date + + invoice.subscription_request.state = "paid" + invoice.post_process_confirm_paid(effective_date) + # if there is a open refund we mark the subscription as cancelled + elif ( + invoice.partner_id.cooperator + and invoice.release_capital_request + and invoice.type == "out_invoice" + and refund + ): + invoice.subscription_request.state = "cancelled" + return True diff --git a/easy_my_coop/models/account_journal.py b/easy_my_coop/models/account_journal.py index 9ec7ae1..8c85fa4 100644 --- a/easy_my_coop/models/account_journal.py +++ b/easy_my_coop/models/account_journal.py @@ -1,14 +1,13 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import fields, models - - -class AccountJournal(models.Model): - _inherit = "account.journal" - - get_cooperator_payment = fields.Boolean('Get cooperator payments?') - get_general_payment = fields.Boolean(string='Get general payments?') +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import fields, models + + +class AccountJournal(models.Model): + _inherit = "account.journal" + + get_cooperator_payment = fields.Boolean("Get cooperator payments?") + get_general_payment = fields.Boolean(string="Get general payments?") diff --git a/easy_my_coop/models/company.py b/easy_my_coop/models/company.py index 843f5b0..312d315 100644 --- a/easy_my_coop/models/company.py +++ b/easy_my_coop/models/company.py @@ -1,83 +1,93 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import api, fields, models - - -class ResCompany(models.Model): - _inherit = 'res.company' - - def _get_base_logo(self): - base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') - self.logo_url = base_url + "/logo.png" - - coop_email_contact = fields.Char(string="Contact email address for the" - " cooperator") - subscription_maximum_amount = fields.Float(string="Maximum authorised" - " subscription amount") - default_country_id = fields.Many2one('res.country', - string="Default country", - default=lambda self: self.country_id) - default_lang_id = fields.Many2one('res.lang', - string="Default lang") - allow_id_card_upload = fields.Boolean(string="Allow ID Card upload") - create_user = fields.Boolean(string="Create user for cooperator", - default=False) - board_representative = fields.Char(string="Board representative name") - signature_scan = fields.Binary(string="Board representative signature") - property_cooperator_account = fields.Many2one('account.account', - company_dependent=True, - string="Cooperator Account", - domain=[('internal_type', '=', 'receivable'), - ('deprecated', '=', False)], - help="This account will be" - " the default one as the" - " receivable account for the" - " cooperators", - required=True) - unmix_share_type = fields.Boolean(string="Unmix share type", - default=True, - help="If checked, A cooperator will be" - " authorised to have only one type" - " of share") - display_logo1 = fields.Boolean(string="Display logo 1") - display_logo2 = fields.Boolean(string="Display logo 2") - bottom_logo1 = fields.Binary(string="Bottom logo 1") - bottom_logo2 = fields.Binary(string="Bottom logo 2") - logo_url = fields.Char(string="logo url", - compute="_get_base_logo") - display_data_policy_approval = fields.Boolean( - help="Choose to display a data policy checkbox on the cooperator" - " website form." - ) - data_policy_approval_required = fields.Boolean( - string="Is data policy approval required?" - ) - data_policy_approval_text = fields.Html( - translate=True, - help="Text to display aside the checkbox to approve data policy." - ) - display_internal_rules_approval = fields.Boolean( - help="Choose to display an internal rules checkbox on the" - " cooperator website form." - ) - internal_rules_approval_required = fields.Boolean( - string="Is internal rules approval required?" - ) - internal_rules_approval_text = fields.Html( - translate=True, - help="Text to display aside the checkbox to approve internal rules." - ) - - @api.onchange('data_policy_approval_required') - def onchange_data_policy_approval_required(self): - if self.data_policy_approval_required: - self.display_data_policy_approval = True - - @api.onchange('internal_rules_approval_required') - def onchange_internal_rules_approval_required(self): - if self.internal_rules_approval_required: - self.display_internal_rules_approval = True +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + def _get_base_logo(self): + base_url = ( + self.env["ir.config_parameter"].sudo().get_param("web.base.url") + ) + self.logo_url = base_url + "/logo.png" + + coop_email_contact = fields.Char( + string="Contact email address for the" " cooperator" + ) + subscription_maximum_amount = fields.Float( + string="Maximum authorised" " subscription amount" + ) + default_country_id = fields.Many2one( + "res.country", + string="Default country", + default=lambda self: self.country_id, + ) + default_lang_id = fields.Many2one("res.lang", string="Default lang") + allow_id_card_upload = fields.Boolean(string="Allow ID Card upload") + create_user = fields.Boolean( + string="Create user for cooperator", default=False + ) + board_representative = fields.Char(string="Board representative name") + signature_scan = fields.Binary(string="Board representative signature") + property_cooperator_account = fields.Many2one( + "account.account", + company_dependent=True, + string="Cooperator Account", + domain=[ + ("internal_type", "=", "receivable"), + ("deprecated", "=", False), + ], + help="This account will be" + " the default one as the" + " receivable account for the" + " cooperators", + required=True, + ) + unmix_share_type = fields.Boolean( + string="Unmix share type", + default=True, + help="If checked, A cooperator will be" + " authorised to have only one type" + " of share", + ) + display_logo1 = fields.Boolean(string="Display logo 1") + display_logo2 = fields.Boolean(string="Display logo 2") + bottom_logo1 = fields.Binary(string="Bottom logo 1") + bottom_logo2 = fields.Binary(string="Bottom logo 2") + logo_url = fields.Char(string="logo url", compute="_get_base_logo") + display_data_policy_approval = fields.Boolean( + help="Choose to display a data policy checkbox on the cooperator" + " website form." + ) + data_policy_approval_required = fields.Boolean( + string="Is data policy approval required?" + ) + data_policy_approval_text = fields.Html( + translate=True, + help="Text to display aside the checkbox to approve data policy.", + ) + display_internal_rules_approval = fields.Boolean( + help="Choose to display an internal rules checkbox on the" + " cooperator website form." + ) + internal_rules_approval_required = fields.Boolean( + string="Is internal rules approval required?" + ) + internal_rules_approval_text = fields.Html( + translate=True, + help="Text to display aside the checkbox to approve internal rules.", + ) + + @api.onchange("data_policy_approval_required") + def onchange_data_policy_approval_required(self): + if self.data_policy_approval_required: + self.display_data_policy_approval = True + + @api.onchange("internal_rules_approval_required") + def onchange_internal_rules_approval_required(self): + if self.internal_rules_approval_required: + self.display_internal_rules_approval = True diff --git a/easy_my_coop/models/coop.py b/easy_my_coop/models/coop.py index 3b7376d..ce3b1f8 100644 --- a/easy_my_coop/models/coop.py +++ b/easy_my_coop/models/coop.py @@ -1,776 +1,937 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models, _ -from addons.base_iban.models.res_partner_bank import validate_iban -from odoo.exceptions import UserError, ValidationError - -_REQUIRED = ['email', - 'firstname', - 'lastname', - 'birthdate', - 'address', - 'share_product_id', - 'ordered_parts', - 'zip_code', - 'city', - 'iban', - 'gender'] - - -@api.model -def _lang_get(self): - languages = self.env['res.lang'].search([]) - return [(language.code, language.name) for language in languages] - - -# todo move to subscription_request.py -class SubscriptionRequest(models.Model): - _name = 'subscription.request' - _description = 'Subscription Request' - - def get_required_field(self): - required_fields = _REQUIRED - company = self.env['res.company']._company_default_get() - if company.data_policy_approval_required: - required_fields.append('data_policy_approved') - if company.internal_rules_approval_required: - required_fields.append('internal_rules_approved') - return required_fields - - def get_mail_template_notif(self, is_company=False): - if is_company: - mail_template = 'easy_my_coop.email_template_confirmation_company' - else: - mail_template = 'easy_my_coop.email_template_confirmation' - return self.env.ref(mail_template, False) - - def is_member(self, vals, cooperator): - if cooperator.member: - vals['type'] = 'increase' - vals['already_cooperator'] = True - return vals - - @api.model - def create(self, vals): - partner_obj = self.env['res.partner'] - - if not vals.get('partner_id'): - cooperator = False - if vals.get('email'): - cooperator = partner_obj.get_cooperator_from_email( - vals.get('email')) - if cooperator: - # TODO remove the following line once it has - # been found a way to avoid double encoding - cooperator = cooperator[0] - vals['type'] = 'subscription' - vals = self.is_member(vals, cooperator) - vals['partner_id'] = cooperator.id - else: - cooperator_id = vals.get('partner_id') - cooperator = partner_obj.browse(cooperator_id) - vals = self.is_member(vals, cooperator) - - if not cooperator.cooperator: - cooperator.write({'cooperator': True}) - subscr_request = super(SubscriptionRequest, self).create(vals) - - mail_template_notif = subscr_request.get_mail_template_notif(False) - mail_template_notif.send_mail(subscr_request.id) - - return subscr_request - - @api.model - def create_comp_sub_req(self, vals): - vals["name"] = vals['company_name'] - if not vals.get('partner_id'): - cooperator = self.env['res.partner'].get_cooperator_from_crn(vals.get('company_register_number')) - if cooperator: - vals['partner_id'] = cooperator.id - vals['type'] = 'increase' - vals['already_cooperator'] = True - subscr_request = super(SubscriptionRequest, self).create(vals) - - confirmation_mail_template = subscr_request.get_mail_template_notif(True) - confirmation_mail_template.send_mail(subscr_request.id) - - return subscr_request - - def check_empty_string(self, value): - if value is None or value is False or value == '': - return False - return True - - def check_iban(self, iban): - try: - if iban: - validate_iban(iban) - return True - else: - return False - except ValidationError: - return False - - @api.multi - @api.depends('iban', 'skip_control_ng') - def _validated_lines(self): - for sub_request in self: - validated = ( - sub_request.skip_control_ng - or self.check_iban(sub_request.iban) - ) - sub_request.validated = validated - - @api.multi - @api.depends('share_product_id', - 'share_product_id.list_price', - 'ordered_parts') - def _compute_subscription_amount(self): - for sub_request in self: - sub_request.subscription_amount = (sub_request.share_product_id. - list_price * - sub_request.ordered_parts) - - already_cooperator = fields.Boolean(string="I'm already cooperator", - readonly=True, - states={'draft': [('readonly', False)]} - ) - name = fields.Char(string='Name', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - firstname = fields.Char(string='Firstname', - readonly=True, - states={'draft': [('readonly', False)]}) - lastname = fields.Char(string='Lastname', - readonly=True, - states={'draft': [('readonly', False)]}) - birthdate = fields.Date(string="Birthdate", - readonly=True, - states={'draft': [('readonly', False)]}) - gender = fields.Selection([('male', _('Male')), - ('female', _('Female')), - ('other', _('Other'))], - string='Gender', - readonly=True, - states={'draft': [('readonly', False)]}) - type = fields.Selection([('new', 'New Cooperator'), - ('subscription', 'Subscription'), - ('increase', 'Increase number of share')], - string='Type', default="new", - readonly=True, - states={'draft': [('readonly', False)]}) - state = fields.Selection([('draft', 'Draft'), - ('block', 'Blocked'), - ('done', 'Done'), - ('waiting', 'Waiting'), - ('transfer', 'Transfer'), - ('cancelled', 'Cancelled'), - ('paid', 'paid')], - string='State', required=True, default="draft") - email = fields.Char(string='Email', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - iban = fields.Char(string='Account Number', - readonly=True, - states={'draft': [('readonly', False)]}) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - readonly=True, - states={'draft': [('readonly', False)]}) - share_product_id = fields.Many2one('product.product', - string='Share type', - domain=[('is_share', '=', True)], - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True, - states={'draft': [('readonly', False)]}) - share_unit_price = fields.Float(related='share_product_id.list_price', - string='Share price', - readonly=True, - states={'draft': [('readonly', False)]}) - subscription_amount = fields.Monetary( - compute='_compute_subscription_amount', - string='Subscription amount', - currency_field="company_currency_id", - readonly=True, - states={'draft': [('readonly', False)]}, - ) - ordered_parts = fields.Integer(string='Number of Share', - required=True, - readonly=True, - default=1, - states={'draft': [('readonly', False)]}) - address = fields.Char(string='Address', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - city = fields.Char(string='City', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - zip_code = fields.Char(string='Zip Code', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - country_id = fields.Many2one('res.country', - string='Country', - ondelete='restrict', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}) - phone = fields.Char(string='Phone', - readonly=True, - states={'draft': [('readonly', False)]}) - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True) - # todo rename to valid_subscription_request - validated = fields.Boolean(compute='_validated_lines', - string='Valid Subscription request?', - readonly=True) - skip_control_ng = fields.Boolean(string="Skip control", - help="if this field is checked then no" - " control will be done on the national" - " register number and on the iban bank" - " account. To be done in case of the id" - " card is from abroad or in case of" - " a passport") - lang = fields.Selection(_lang_get, - string='Language', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}, - default=lambda self: self.env['res.company']._company_default_get().default_lang_id.code) - date = fields.Date(string='Subscription date request', - required=True, - readonly=True, - states={'draft': [('readonly', False)]}, - default=lambda self: datetime.strftime(datetime.now(), - '%Y-%m-%d')) - 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()) - company_currency_id = fields.Many2one( - "res.currency", - related="company_id.currency_id", - string="Company Currency", - readonly=True, - ) - is_company = fields.Boolean(string='Is a company', - readonly=True, - states={'draft': [('readonly', False)]}) - is_operation = fields.Boolean(string='Is an operation', - readonly=True, - states={'draft': [('readonly', False)]}) - company_name = fields.Char(string="Company name", - readonly=True, - states={'draft': [('readonly', False)]}) - company_email = fields.Char(string="Company email", - readonly=True, - states={'draft': [('readonly', False)]}) - company_register_number = fields.Char(string='Company register number', - readonly=True, - states={'draft': [('readonly', False)]}) - company_type = fields.Selection([('', '')], - string="Company type", - readonly=True, - states={'draft': [('readonly', False)]}) - same_address = fields.Boolean(string='Same address', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_address = fields.Char(string='Activities address', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_city = fields.Char(string='Activities city', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_zip_code = fields.Char(string='Activities zip Code', - readonly=True, - states={'draft': [('readonly', False)]}) - activities_country_id = fields.Many2one('res.country', - string='Activities country', - ondelete='restrict', - readonly=True, - states={'draft': [('readonly', False)]}) - contact_person_function = fields.Char(string='Function', - readonly=True, - states={'draft': [('readonly', False)]}) - operation_request_id = fields.Many2one('operation.request', - string="Operation Request", - readonly=True, - states={'draft': [('readonly', False)]}) - capital_release_request = fields.One2many('account.invoice', - 'subscription_request', - string='Capital release request', - readonly=True, - states={'draft': [('readonly', False)]}) - capital_release_request_date = fields.Date(string="Force the capital " - "release request date", - help="Keep empty to use the " - "current date", - copy=False, - readonly=True, - states={'draft': [('readonly', False)]}) - source = fields.Selection([('website', 'Website'), - ('crm', 'CRM'), - ('manual', 'Manual'), - ('operation', 'Operation')], - string="Source", - default="website", - readonly=True, - states={'draft': [('readonly', False)]}) - data_policy_approved = fields.Boolean( - string='Data Policy Approved', - default=False, - ) - internal_rules_approved = fields.Boolean( - string='Approved Internal Rules', - default=False, - ) - _order = "id desc" - - def get_person_info(self, partner): - self.firstname = partner.firstname - self.name = partner.name - self.lastname = partner.lastname - self.email = partner.email - self.birthdate = partner.birthdate_date - self.gender = partner.gender - self.address = partner.street - self.city = partner.city - self.zip_code = partner.zip - self.country_id = partner.country_id - self.phone = partner.phone - self.lang = partner.lang - - @api.onchange('partner_id') - def onchange_partner(self): - partner = self.partner_id - if partner: - self.is_company = partner.is_company - self.already_cooperator = partner.member - if partner.bank_ids: - self.iban = partner.bank_ids[0].acc_number - if partner.member: - self.type = 'increase' - if partner.is_company: - self.company_name = partner.name - self.company_email = partner.email - self.company_register_number = partner.company_register_number - representative = partner.get_representative() - self.get_person_info(representative) - self.contact_person_function = representative.function - else: - self.get_person_info(partner) - - # declare this function in order to be overriden - def get_eater_vals(self, partner, share_product_id): #noqa - return {} - - def _prepare_invoice_line(self, product, partner, qty): - self.ensure_one() - account = product.property_account_income_id \ - or product.categ_id.property_account_income_categ_id - if not account: - raise UserError(_('Please define income account for this product:' - ' "%s" (id:%d) - or for its category: "%s".') % - (product.name, product.id, product.categ_id.name)) - - fpos = partner.property_account_position_id - if fpos: - account = fpos.map_account(account) - - res = { - 'name': product.name, - 'account_id': account.id, - 'price_unit': product.lst_price, - 'quantity': qty, - 'uom_id': product.uom_id.id, - 'product_id': product.id or False, - } - return res - - def get_capital_release_mail_template(self): - template = 'easy_my_coop.email_template_release_capital' - return self.env.ref(template, False) - - def send_capital_release_request(self, invoice): - email_template = self.get_capital_release_mail_template() - - # we send the email with the capital release request in attachment - # TODO remove sudo() and give necessary access right - email_template.sudo().send_mail(invoice.id, True) - invoice.sent = True - - def get_journal(self): - return self.env['account.journal'].search([('code', '=', 'SUBJ')])[0] - - def get_accounting_account(self): - account_obj = self.env['account.account'] - if self.company_id.property_cooperator_account: - account = self.company_id.property_cooperator_account - else: - accounts = account_obj.search([('code', '=', '416000')]) - if accounts: - account = accounts[0] - else: - raise UserError(_( - 'You must set a cooperator account on you company.' - )) - return account - - def get_invoice_vals(self, partner): - return { - 'partner_id': partner.id, - 'journal_id': self.get_journal().id, - 'account_id': self.get_accounting_account().id, - 'type': 'out_invoice', - 'release_capital_request': True, - 'subscription_request': self.id - } - - def create_invoice(self, partner): - # creating invoice and invoice lines - invoice_vals = self.get_invoice_vals(partner) - if self.capital_release_request_date: - invoice_vals['date_invoice'] = self.capital_release_request_date - invoice = self.env['account.invoice'].create(invoice_vals) - vals = self._prepare_invoice_line(self.share_product_id, partner, - self.ordered_parts) - vals['invoice_id'] = invoice.id - self.env['account.invoice.line'].create(vals) - - # validate the capital release request - invoice.action_invoice_open() - - self.send_capital_release_request(invoice) - - return invoice - - def get_partner_company_vals(self): - partner_vals = {'name': self.company_name, - 'last_name': self.company_name, - 'is_company': self.is_company, - 'company_register_number': self.company_register_number, # noqa - 'customer': False, 'cooperator': True, - 'street': self.address, 'zip': self.zip_code, - 'city': self.city, 'email': self.company_email, - 'out_inv_comm_type': 'bba', - 'customer': self.share_product_id.customer, - 'country_id': self.country_id.id, - 'lang': self.lang, - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return partner_vals - - def get_partner_vals(self): - partner_vals = {'name': self.name, 'firstname': self.firstname, - 'lastname': self.lastname, 'street': self.address, - 'zip': self.zip_code, 'email': self.email, - 'gender': self.gender, 'cooperator': True, - 'city': self.city, 'phone': self.phone, - 'country_id': self.country_id.id, 'lang': self.lang, - 'birthdate_date': self.birthdate, - 'customer': self.share_product_id.customer, - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return partner_vals - - def get_representative_vals(self): - contact_vals = { - 'name': self.name, - 'firstname': self.firstname, - 'lastname': self.lastname, 'customer': False, - 'is_company': False, 'cooperator': True, - 'street': self.address, 'gender': self.gender, - 'zip': self.zip_code, 'city': self.city, - 'phone': self.phone, 'email': self.email, - 'country_id': self.country_id.id, - 'out_inv_comm_type': 'bba', - 'out_inv_comm_algorithm': 'random', - 'lang': self.lang, - 'birthdate_date': self.birthdate, - 'parent_id': self.partner_id.id, - 'representative': True, - 'function': self.contact_person_function, - 'type': 'representative', - 'data_policy_approved': self.data_policy_approved, - 'internal_rules_approved': self.internal_rules_approved - } - return contact_vals - - def create_coop_partner(self): - partner_obj = self.env['res.partner'] - - if self.is_company: - partner_vals = self.get_partner_company_vals() - else: - partner_vals = self.get_partner_vals() - - partner = partner_obj.create(partner_vals) - if self.iban: - self.env['res.partner.bank'].create({ - 'partner_id': partner.id, - 'acc_number': self.iban - }) - return partner - - def set_membership(self): - # To be overridden - return True - - @api.one # todo remove api.one - def validate_subscription_request(self): - # todo rename to validate (careful with iwp dependencies) - partner_obj = self.env['res.partner'] - - if self.ordered_parts <= 0: - raise UserError(_('Number of share must be greater than 0.')) - if self.partner_id: - partner = self.partner_id - else: - partner = None - domain = [] - if self.already_cooperator: - raise UserError(_('The checkbox already cooperator is' - ' checked please select a cooperator.')) - elif self.is_company and self.company_register_number: - domain = [('company_register_number', '=', self.company_register_number)] # noqa - elif not self.is_company and self.email: - domain = [('email', '=', self.email)] - - if domain: - partner = partner_obj.search(domain) - - if not partner: - partner = self.create_coop_partner() - self.partner_id = partner - else: - partner = partner[0] - - partner.cooperator = True - - if self.is_company and not partner.has_representative(): - contact = False - if self.email: - domain = [('email', '=', self.email)] - contact = partner_obj.search(domain) - if contact: - contact.type = 'representative' - if not contact: - contact_vals = self.get_representative_vals() - partner_obj.create(contact_vals) - else: - if len(contact) > 1: - raise UserError(_('There is two different persons with the' - ' same national register number. Please' - ' proceed to a merge before to continue') - ) - if contact.parent_id and contact.parent_id.id != partner.id: - raise UserError(_('This contact person is already defined' - ' for another company. Please select' - ' another contact')) - else: - contact.write({'parent_id': partner.id, - 'representative': True}) - - invoice = self.create_invoice(partner) - self.write({'state': 'done'}) - self.set_membership() - - return invoice - - @api.one # todo remove api.one - def block_subscription_request(self): - self.write({'state': 'block'}) - - @api.one # todo remove api.one - def unblock_subscription_request(self): - self.write({'state': 'draft'}) - - @api.one # todo remove api.one - def cancel_subscription_request(self): - self.write({'state': 'cancelled'}) - - @api.one # todo remove api.one - def put_on_waiting_list(self): - waiting_list_mail_template = self.env.ref('easy_my_coop.email_template_waiting_list', False) - waiting_list_mail_template.send_mail(self.id, True) - self.write({'state': 'waiting'}) - - -# todo move to share_line.py -class ShareLine(models.Model): - _name = 'share.line' - _description = "Share line" - - @api.multi - def _compute_total_line(self): - res = {} - for line in self: - line.total_amount_line = line.share_unit_price * line.share_number - return res - - share_product_id = fields.Many2one('product.product', - string='Share type', - required=True, - readonly=True) - share_number = fields.Integer(string='Number of Share', - required=True, - readonly=True) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True) - share_unit_price = fields.Monetary( - string='Share price', - currency_field="company_currency_id", - readonly=True, - ) - effective_date = fields.Date(string='Effective Date', - readonly=True) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - required=True, - ondelete='cascade', - readonly=True) - total_amount_line = fields.Monetary( - string='Total amount line', - currency_field="company_currency_id", - compute='_compute_total_line', - ) - 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(), - ) - company_currency_id = fields.Many2one( - "res.currency", - string="Company Currency", - related="company_id.currency_id", - readonly=True, - ) - - -# todo move to subscription_register.py -class SubscriptionRegister(models.Model): - _name = 'subscription.register' - _description = "Subscription register" - - @api.multi - def _compute_total_line(self): - for line in self: - line.total_amount_line = line.share_unit_price * line.quantity - - name = fields.Char(string='Number Operation', - required=True, - readonly=True) - register_number_operation = fields.Integer(string='Register Number Operation', - required=True, - readonly=True) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - required=True, - readonly=True) - partner_id_to = fields.Many2one('res.partner', - string='Transfered to', - readonly=True) - date = fields.Date(string='Subscription Date', - required=True, - readonly=True) - quantity = fields.Integer(string='Number of share', - readonly=True) - share_unit_price = fields.Monetary( - string='Share price', - currency_field="company_currency_id", - readonly=True, - ) - total_amount_line = fields.Monetary( - string='Total amount line', - currency_field="company_currency_id", - compute='_compute_total_line', - ) - share_product_id = fields.Many2one('product.product', - string='Share type', - required=True, - readonly=True, - domain=[('is_share', '=', True)]) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name', - readonly=True) - share_to_product_id = fields.Many2one('product.product', - string='Share to type', - readonly=True, - domain=[('is_share', '=', True)]) - share_to_short_name = fields.Char(related='share_to_product_id.short_name', - string='Share to type name', - readonly=True) - quantity_to = fields.Integer(string='Number of share to', - readonly=True) - share_to_unit_price = fields.Monetary( - string='Share to price', - currency_field="company_currency_id", - readonly=True, - ) - type = fields.Selection([('subscription', 'Subscription'), - ('transfer', 'Transfer'), - ('sell_back', 'Sell Back'), - ('convert', 'Conversion')], - string='Operation Type', readonly=True) - 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()) - company_currency_id = fields.Many2one( - "res.currency", - related="company_id.currency_id", - string="Company Currency", - readonly=True, - ) - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True, - default=lambda self: self.env.user) - - _order = "register_number_operation asc" - - @api.model - def read_group(self, domain, fields, groupby, offset=0, limit=None, - orderby=False, - lazy=True): - if 'share_unit_price' in fields: - fields.remove('share_unit_price') - if 'register_number_operation' in fields: - fields.remove('register_number_operation') - res = super(SubscriptionRegister, self).read_group(domain, fields, - groupby, - offset=offset, - limit=limit, - orderby=orderby, - lazy=lazy) - if 'total_amount_line' in fields: - for line in res: - if '__domain' in line: - lines = self.search(line['__domain']) - inv_value = 0.0 - for line2 in lines: - inv_value += line2.total_amount_line - line['total_amount_line'] = inv_value - return res +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +from addons.base_iban.models.res_partner_bank import validate_iban + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError + +_REQUIRED = [ + "email", + "firstname", + "lastname", + "birthdate", + "address", + "share_product_id", + "ordered_parts", + "zip_code", + "city", + "iban", + "gender", +] + + +@api.model +def _lang_get(self): + languages = self.env["res.lang"].search([]) + return [(language.code, language.name) for language in languages] + + +# todo move to subscription_request.py +class SubscriptionRequest(models.Model): + _name = "subscription.request" + _description = "Subscription Request" + + def get_required_field(self): + required_fields = _REQUIRED + company = self.env["res.company"]._company_default_get() + if company.data_policy_approval_required: + required_fields.append("data_policy_approved") + if company.internal_rules_approval_required: + required_fields.append("internal_rules_approved") + return required_fields + + def get_mail_template_notif(self, is_company=False): + if is_company: + mail_template = "easy_my_coop.email_template_confirmation_company" + else: + mail_template = "easy_my_coop.email_template_confirmation" + return self.env.ref(mail_template, False) + + def is_member(self, vals, cooperator): + if cooperator.member: + vals["type"] = "increase" + vals["already_cooperator"] = True + return vals + + @api.model + def create(self, vals): + partner_obj = self.env["res.partner"] + + if not vals.get("partner_id"): + cooperator = False + if vals.get("email"): + cooperator = partner_obj.get_cooperator_from_email( + vals.get("email") + ) + if cooperator: + # TODO remove the following line once it has + # been found a way to avoid double encoding + cooperator = cooperator[0] + vals["type"] = "subscription" + vals = self.is_member(vals, cooperator) + vals["partner_id"] = cooperator.id + else: + cooperator_id = vals.get("partner_id") + cooperator = partner_obj.browse(cooperator_id) + vals = self.is_member(vals, cooperator) + + if not cooperator.cooperator: + cooperator.write({"cooperator": True}) + subscr_request = super(SubscriptionRequest, self).create(vals) + + mail_template_notif = subscr_request.get_mail_template_notif(False) + mail_template_notif.send_mail(subscr_request.id) + + return subscr_request + + @api.model + def create_comp_sub_req(self, vals): + vals["name"] = vals["company_name"] + if not vals.get("partner_id"): + cooperator = self.env["res.partner"].get_cooperator_from_crn( + vals.get("company_register_number") + ) + if cooperator: + vals["partner_id"] = cooperator.id + vals["type"] = "increase" + vals["already_cooperator"] = True + subscr_request = super(SubscriptionRequest, self).create(vals) + + confirmation_mail_template = subscr_request.get_mail_template_notif( + True + ) + confirmation_mail_template.send_mail(subscr_request.id) + + return subscr_request + + def check_empty_string(self, value): + if value is None or value is False or value == "": + return False + return True + + def check_iban(self, iban): + try: + if iban: + validate_iban(iban) + return True + else: + return False + except ValidationError: + return False + + @api.multi + @api.depends("iban", "skip_control_ng") + def _validated_lines(self): + for sub_request in self: + validated = sub_request.skip_control_ng or self.check_iban( + sub_request.iban + ) + sub_request.validated = validated + + @api.multi + @api.depends( + "share_product_id", "share_product_id.list_price", "ordered_parts" + ) + def _compute_subscription_amount(self): + for sub_request in self: + sub_request.subscription_amount = ( + sub_request.share_product_id.list_price + * sub_request.ordered_parts + ) + + already_cooperator = fields.Boolean( + string="I'm already cooperator", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + name = fields.Char( + string="Name", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + firstname = fields.Char( + string="Firstname", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + lastname = fields.Char( + string="Lastname", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + birthdate = fields.Date( + string="Birthdate", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + gender = fields.Selection( + [("male", _("Male")), ("female", _("Female")), ("other", _("Other"))], + string="Gender", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + type = fields.Selection( + [ + ("new", "New Cooperator"), + ("subscription", "Subscription"), + ("increase", "Increase number of share"), + ], + string="Type", + default="new", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("block", "Blocked"), + ("done", "Done"), + ("waiting", "Waiting"), + ("transfer", "Transfer"), + ("cancelled", "Cancelled"), + ("paid", "paid"), + ], + string="State", + required=True, + default="draft", + ) + email = fields.Char( + string="Email", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + iban = fields.Char( + string="Account Number", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + domain=[("is_share", "=", True)], + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + share_unit_price = fields.Float( + related="share_product_id.list_price", + string="Share price", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + subscription_amount = fields.Monetary( + compute="_compute_subscription_amount", + string="Subscription amount", + currency_field="company_currency_id", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + ordered_parts = fields.Integer( + string="Number of Share", + required=True, + readonly=True, + default=1, + states={"draft": [("readonly", False)]}, + ) + address = fields.Char( + string="Address", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + city = fields.Char( + string="City", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + zip_code = fields.Char( + string="Zip Code", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + country_id = fields.Many2one( + "res.country", + string="Country", + ondelete="restrict", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + phone = fields.Char( + string="Phone", readonly=True, states={"draft": [("readonly", False)]} + ) + user_id = fields.Many2one("res.users", string="Responsible", readonly=True) + # todo rename to valid_subscription_request + validated = fields.Boolean( + compute="_validated_lines", + string="Valid Subscription request?", + readonly=True, + ) + skip_control_ng = fields.Boolean( + string="Skip control", + help="if this field is checked then no" + " control will be done on the national" + " register number and on the iban bank" + " account. To be done in case of the id" + " card is from abroad or in case of" + " a passport", + ) + lang = fields.Selection( + _lang_get, + string="Language", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default=lambda self: self.env["res.company"] + ._company_default_get() + .default_lang_id.code, + ) + date = fields.Date( + string="Subscription date request", + required=True, + readonly=True, + states={"draft": [("readonly", False)]}, + default=lambda self: datetime.strftime(datetime.now(), "%Y-%m-%d"), + ) + 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(), + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + is_company = fields.Boolean( + string="Is a company", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + is_operation = fields.Boolean( + string="Is an operation", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_name = fields.Char( + string="Company name", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_email = fields.Char( + string="Company email", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_register_number = fields.Char( + string="Company register number", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + company_type = fields.Selection( + [("", "")], + string="Company type", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + same_address = fields.Boolean( + string="Same address", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_address = fields.Char( + string="Activities address", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_city = fields.Char( + string="Activities city", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_zip_code = fields.Char( + string="Activities zip Code", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + activities_country_id = fields.Many2one( + "res.country", + string="Activities country", + ondelete="restrict", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + contact_person_function = fields.Char( + string="Function", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + operation_request_id = fields.Many2one( + "operation.request", + string="Operation Request", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + capital_release_request = fields.One2many( + "account.invoice", + "subscription_request", + string="Capital release request", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + capital_release_request_date = fields.Date( + string="Force the capital " "release request date", + help="Keep empty to use the " "current date", + copy=False, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + source = fields.Selection( + [ + ("website", "Website"), + ("crm", "CRM"), + ("manual", "Manual"), + ("operation", "Operation"), + ], + string="Source", + default="website", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + data_policy_approved = fields.Boolean( + string="Data Policy Approved", default=False + ) + internal_rules_approved = fields.Boolean( + string="Approved Internal Rules", default=False + ) + _order = "id desc" + + def get_person_info(self, partner): + self.firstname = partner.firstname + self.name = partner.name + self.lastname = partner.lastname + self.email = partner.email + self.birthdate = partner.birthdate_date + self.gender = partner.gender + self.address = partner.street + self.city = partner.city + self.zip_code = partner.zip + self.country_id = partner.country_id + self.phone = partner.phone + self.lang = partner.lang + + @api.onchange("partner_id") + def onchange_partner(self): + partner = self.partner_id + if partner: + self.is_company = partner.is_company + self.already_cooperator = partner.member + if partner.bank_ids: + self.iban = partner.bank_ids[0].acc_number + if partner.member: + self.type = "increase" + if partner.is_company: + self.company_name = partner.name + self.company_email = partner.email + self.company_register_number = partner.company_register_number + representative = partner.get_representative() + self.get_person_info(representative) + self.contact_person_function = representative.function + else: + self.get_person_info(partner) + + # declare this function in order to be overriden + def get_eater_vals(self, partner, share_product_id): # noqa + return {} + + def _prepare_invoice_line(self, product, partner, qty): + self.ensure_one() + account = ( + product.property_account_income_id + or product.categ_id.property_account_income_categ_id + ) + if not account: + raise UserError( + _( + "Please define income account for this product:" + ' "%s" (id:%d) - or for its category: "%s".' + ) + % (product.name, product.id, product.categ_id.name) + ) + + fpos = partner.property_account_position_id + if fpos: + account = fpos.map_account(account) + + res = { + "name": product.name, + "account_id": account.id, + "price_unit": product.lst_price, + "quantity": qty, + "uom_id": product.uom_id.id, + "product_id": product.id or False, + } + return res + + def get_capital_release_mail_template(self): + template = "easy_my_coop.email_template_release_capital" + return self.env.ref(template, False) + + def send_capital_release_request(self, invoice): + email_template = self.get_capital_release_mail_template() + + # we send the email with the capital release request in attachment + # TODO remove sudo() and give necessary access right + email_template.sudo().send_mail(invoice.id, True) + invoice.sent = True + + def get_journal(self): + return self.env["account.journal"].search([("code", "=", "SUBJ")])[0] + + def get_accounting_account(self): + account_obj = self.env["account.account"] + if self.company_id.property_cooperator_account: + account = self.company_id.property_cooperator_account + else: + accounts = account_obj.search([("code", "=", "416000")]) + if accounts: + account = accounts[0] + else: + raise UserError( + _("You must set a cooperator account on you company.") + ) + return account + + def get_invoice_vals(self, partner): + return { + "partner_id": partner.id, + "journal_id": self.get_journal().id, + "account_id": self.get_accounting_account().id, + "type": "out_invoice", + "release_capital_request": True, + "subscription_request": self.id, + } + + def create_invoice(self, partner): + # creating invoice and invoice lines + invoice_vals = self.get_invoice_vals(partner) + if self.capital_release_request_date: + invoice_vals["date_invoice"] = self.capital_release_request_date + invoice = self.env["account.invoice"].create(invoice_vals) + vals = self._prepare_invoice_line( + self.share_product_id, partner, self.ordered_parts + ) + vals["invoice_id"] = invoice.id + self.env["account.invoice.line"].create(vals) + + # validate the capital release request + invoice.action_invoice_open() + + self.send_capital_release_request(invoice) + + return invoice + + def get_partner_company_vals(self): + partner_vals = { + "name": self.company_name, + "last_name": self.company_name, + "is_company": self.is_company, + "company_register_number": self.company_register_number, # noqa + "cooperator": True, + "street": self.address, + "zip": self.zip_code, + "city": self.city, + "email": self.company_email, + "out_inv_comm_type": "bba", + "customer": self.share_product_id.customer, + "country_id": self.country_id.id, + "lang": self.lang, + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return partner_vals + + def get_partner_vals(self): + partner_vals = { + "name": self.name, + "firstname": self.firstname, + "lastname": self.lastname, + "street": self.address, + "zip": self.zip_code, + "email": self.email, + "gender": self.gender, + "cooperator": True, + "city": self.city, + "phone": self.phone, + "country_id": self.country_id.id, + "lang": self.lang, + "birthdate_date": self.birthdate, + "customer": self.share_product_id.customer, + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return partner_vals + + def get_representative_vals(self): + contact_vals = { + "name": self.name, + "firstname": self.firstname, + "lastname": self.lastname, + "customer": False, + "is_company": False, + "cooperator": True, + "street": self.address, + "gender": self.gender, + "zip": self.zip_code, + "city": self.city, + "phone": self.phone, + "email": self.email, + "country_id": self.country_id.id, + "out_inv_comm_type": "bba", + "out_inv_comm_algorithm": "random", + "lang": self.lang, + "birthdate_date": self.birthdate, + "parent_id": self.partner_id.id, + "representative": True, + "function": self.contact_person_function, + "type": "representative", + "data_policy_approved": self.data_policy_approved, + "internal_rules_approved": self.internal_rules_approved, + } + return contact_vals + + def create_coop_partner(self): + partner_obj = self.env["res.partner"] + + if self.is_company: + partner_vals = self.get_partner_company_vals() + else: + partner_vals = self.get_partner_vals() + + partner = partner_obj.create(partner_vals) + if self.iban: + self.env["res.partner.bank"].create( + {"partner_id": partner.id, "acc_number": self.iban} + ) + return partner + + def set_membership(self): + # To be overridden + return True + + @api.multi + def validate_subscription_request(self): + self.ensure_one() + # todo rename to validate (careful with iwp dependencies) + partner_obj = self.env["res.partner"] + + if self.ordered_parts <= 0: + raise UserError(_("Number of share must be greater than 0.")) + if self.partner_id: + partner = self.partner_id + else: + partner = None + domain = [] + if self.already_cooperator: + raise UserError( + _( + "The checkbox already cooperator is" + " checked please select a cooperator." + ) + ) + elif self.is_company and self.company_register_number: + domain = [ + ( + "company_register_number", + "=", + self.company_register_number, + ) + ] # noqa + elif not self.is_company and self.email: + domain = [("email", "=", self.email)] + + if domain: + partner = partner_obj.search(domain) + + if not partner: + partner = self.create_coop_partner() + self.partner_id = partner + else: + partner = partner[0] + + partner.cooperator = True + + if self.is_company and not partner.has_representative(): + contact = False + if self.email: + domain = [("email", "=", self.email)] + contact = partner_obj.search(domain) + if contact: + contact.type = "representative" + if not contact: + contact_vals = self.get_representative_vals() + partner_obj.create(contact_vals) + else: + if len(contact) > 1: + raise UserError( + _( + "There is two different persons with the" + " same national register number. Please" + " proceed to a merge before to continue" + ) + ) + if contact.parent_id and contact.parent_id.id != partner.id: + raise UserError( + _( + "This contact person is already defined" + " for another company. Please select" + " another contact" + ) + ) + else: + contact.write( + {"parent_id": partner.id, "representative": True} + ) + + invoice = self.create_invoice(partner) + self.write({"state": "done"}) + self.set_membership() + + return invoice + + @api.multi + def block_subscription_request(self): + self.ensure_one() + self.write({"state": "block"}) + + @api.multi + def unblock_subscription_request(self): + self.ensure_one() + self.write({"state": "draft"}) + + @api.multi + def cancel_subscription_request(self): + self.ensure_one() + self.write({"state": "cancelled"}) + + @api.multi + def put_on_waiting_list(self): + self.ensure_one() + waiting_list_mail_template = self.env.ref( + "easy_my_coop.email_template_waiting_list", False + ) + waiting_list_mail_template.send_mail(self.id, True) + self.write({"state": "waiting"}) + + +# todo move to share_line.py +class ShareLine(models.Model): + _name = "share.line" + _description = "Share line" + + @api.multi + def _compute_total_line(self): + res = {} + for line in self: + line.total_amount_line = line.share_unit_price * line.share_number + return res + + share_product_id = fields.Many2one( + "product.product", string="Share type", required=True, readonly=True + ) + share_number = fields.Integer( + string="Number of Share", required=True, readonly=True + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + ) + share_unit_price = fields.Monetary( + string="Share price", + currency_field="company_currency_id", + readonly=True, + ) + effective_date = fields.Date(string="Effective Date", readonly=True) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + required=True, + ondelete="cascade", + readonly=True, + ) + total_amount_line = fields.Monetary( + string="Total amount line", + currency_field="company_currency_id", + compute="_compute_total_line", + ) + 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(), + ) + company_currency_id = fields.Many2one( + "res.currency", + string="Company Currency", + related="company_id.currency_id", + readonly=True, + ) + + +# todo move to subscription_register.py +class SubscriptionRegister(models.Model): + _name = "subscription.register" + _description = "Subscription register" + + @api.multi + def _compute_total_line(self): + for line in self: + line.total_amount_line = line.share_unit_price * line.quantity + + name = fields.Char(string="Number Operation", required=True, readonly=True) + register_number_operation = fields.Integer( + string="Register Number Operation", required=True, readonly=True + ) + partner_id = fields.Many2one( + "res.partner", string="Cooperator", required=True, readonly=True + ) + partner_id_to = fields.Many2one( + "res.partner", string="Transfered to", readonly=True + ) + date = fields.Date( + string="Subscription Date", required=True, readonly=True + ) + quantity = fields.Integer(string="Number of share", readonly=True) + share_unit_price = fields.Monetary( + string="Share price", + currency_field="company_currency_id", + readonly=True, + ) + total_amount_line = fields.Monetary( + string="Total amount line", + currency_field="company_currency_id", + compute="_compute_total_line", + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + required=True, + readonly=True, + domain=[("is_share", "=", True)], + ) + share_short_name = fields.Char( + related="share_product_id.short_name", + string="Share type name", + readonly=True, + ) + share_to_product_id = fields.Many2one( + "product.product", + string="Share to type", + readonly=True, + domain=[("is_share", "=", True)], + ) + share_to_short_name = fields.Char( + related="share_to_product_id.short_name", + string="Share to type name", + readonly=True, + ) + quantity_to = fields.Integer(string="Number of share to", readonly=True) + share_to_unit_price = fields.Monetary( + string="Share to price", + currency_field="company_currency_id", + readonly=True, + ) + type = fields.Selection( + [ + ("subscription", "Subscription"), + ("transfer", "Transfer"), + ("sell_back", "Sell Back"), + ("convert", "Conversion"), + ], + string="Operation Type", + readonly=True, + ) + 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(), + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + user_id = fields.Many2one( + "res.users", + string="Responsible", + readonly=True, + default=lambda self: self.env.user, + ) + + _order = "register_number_operation asc" + + @api.model + def read_group( + self, + domain, + fields, + groupby, + offset=0, + limit=None, + orderby=False, + lazy=True, + ): + if "share_unit_price" in fields: + fields.remove("share_unit_price") + if "register_number_operation" in fields: + fields.remove("register_number_operation") + res = super(SubscriptionRegister, self).read_group( + domain, + fields, + groupby, + offset=offset, + limit=limit, + orderby=orderby, + lazy=lazy, + ) + if "total_amount_line" in fields: + for line in res: + if "__domain" in line: + lines = self.search(line["__domain"]) + inv_value = 0.0 + for line2 in lines: + inv_value += line2.total_amount_line + line["total_amount_line"] = inv_value + return res diff --git a/easy_my_coop/models/operation_request.py b/easy_my_coop/models/operation_request.py index b31dabe..730ef6e 100644 --- a/easy_my_coop/models/operation_request.py +++ b/easy_my_coop/models/operation_request.py @@ -1,335 +1,432 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from datetime import datetime - -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError - - -class operation_request(models.Model): - _name = 'operation.request' - _description = "Operation request" - - def get_date_now(self): - # fixme odoo 12 uses date types - return datetime.strftime(datetime.now(), '%Y-%m-%d') - - @api.multi - @api.depends('share_product_id', 'share_product_id.list_price', 'quantity') - def _compute_subscription_amount(self): - for operation_request in self: - operation_request.subscription_amount = (operation_request. - share_product_id. - list_price * - operation_request. - quantity) - - request_date = fields.Date(string='Request date', - default=lambda self: self.get_date_now()) - partner_id = fields.Many2one('res.partner', - string='Cooperator', - domain=[('member', '=', True)], - required=True) - partner_id_to = fields.Many2one('res.partner', - string='Transfered to', - domain=[('cooperator', '=', True)]) - operation_type = fields.Selection([('subscription', 'Subscription'), - ('transfer', 'Transfer'), - ('sell_back', 'Sell Back'), - ('convert', 'Conversion')], - string='Operation Type', - required=True) - share_product_id = fields.Many2one('product.product', - string='Share type', - domain=[('is_share', '=', True)], - required=True) - share_to_product_id = fields.Many2one('product.product', - string='Convert to this share type', - domain=[('is_share', '=', True)]) - share_short_name = fields.Char(related='share_product_id.short_name', - string='Share type name') - share_to_short_name = fields.Char(related='share_to_product_id.short_name', - string='Share to type name') - share_unit_price = fields.Float(related='share_product_id.list_price', - string='Share price') - share_to_unit_price = fields.Float(related='share_to_product_id.list_price', - string='Share to price') - subscription_amount = fields.Float(compute='_compute_subscription_amount', - string='Operation amount') - quantity = fields.Integer(string='Number of share', - required=True) - state = fields.Selection([('draft', 'Draft'), - ('waiting', 'Waiting'), - ('approved', 'Approved'), - ('done', 'Done'), - ('cancelled', 'Cancelled'), - ('refused', 'Refused')], - string='State', - required=True, - default='draft') - user_id = fields.Many2one('res.users', - string='Responsible', - readonly=True, - default=lambda self: self.env.user) - subscription_request = fields.One2many('subscription.request', - 'operation_request_id', - string="Share Receiver Info", - help="In case on a transfer of" - " share. If the share receiver" - " isn't a effective member then a" - " subscription form should" - " be filled.") - receiver_not_member = fields.Boolean(string='Receiver is not a member') - 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()) - - invoice = fields.Many2one('account.invoice', - string="Invoice") - - @api.multi - def approve_operation(self): - for rec in self: - rec.write({'state': 'approved'}) - - @api.multi - def refuse_operation(self): - for rec in self: - rec.write({'state': 'refused'}) - - @api.multi - def submit_operation(self): - for rec in self: - rec.validate() - rec.write({'state': 'waiting'}) - - @api.multi - def cancel_operation(self): - for rec in self: - rec.write({'state': 'cancelled'}) - - @api.multi - def reset_to_draft(self): - for rec in self: - rec.write({'state': 'draft'}) - - def get_total_share_dic(self, partner): - total_share_dic = {} - share_products = self.env['product.product'].search([('is_share', '=', True)]) - - for share_product in share_products: - total_share_dic[share_product.id] = 0 - - for line in partner.share_ids: - total_share_dic[line.share_product_id.id] += line.share_number - - return total_share_dic - - # This function doesn't handle the case of a cooperator can own - # different kinds of share type - def hand_share_over(self, partner, share_product_id, quantity): - if not partner.member: - raise ValidationError(_("This operation can't be executed if the" - " cooperator is not an effective member")) - - share_ind = len(partner.share_ids) - i = 1 - while quantity > 0: - line = self.partner_id.share_ids[share_ind-i] - if line.share_product_id.id == share_product_id.id: - if quantity > line.share_number: - quantity -= line.share_number - line.unlink() - else: - share_left = line.share_number - quantity - quantity = 0 - line.write({'share_number': share_left}) - i += 1 - # if the cooperator sold all his shares he's no more - # an effective member - remaning_share_dict = 0 - for share_quant in self.get_total_share_dic(partner).values(): - remaning_share_dict += share_quant - if remaning_share_dict == 0: - self.partner_id.write({'member': False, 'old_member': True}) - - def has_share_type(self): - for line in self.partner_id.share_ids: - if line.share_product_id.id == self.share_product_id.id: - return True - return False - - def validate(self): - if not self.has_share_type() and \ - self.operation_type in ['sell_back', 'transfer']: - raise ValidationError(_("The cooperator doesn't own this share" - " type. Please choose the appropriate" - " share type.")) - - if self.operation_type in ['sell_back', 'convert', 'transfer']: - total_share_dic = self.get_total_share_dic(self.partner_id) - - if self.quantity > total_share_dic[self.share_product_id.id]: - raise ValidationError(_("The cooperator can't hand over more" - " shares that he/she owns.")) - - if self.operation_type == 'convert': - if self.company_id.unmix_share_type: - if self.share_product_id.code == self.share_to_product_id.code: - raise ValidationError(_("You can't convert the share to" - " the same share type.")) - if self.subscription_amount != self.partner_id.total_value: - raise ValidationError(_("You must convert all the shares" - " to the selected type.")) - else: - if self.subscription_amount != self.partner_id.total_value: - raise ValidationError(_("Converting just part of the" - " shares is not yet implemented")) - elif self.operation_type == 'transfer': - if not self.receiver_not_member and self.company_id.unmix_share_type \ - and (self.partner_id_to.cooperator_type - and self.partner_id.cooperator_type != self.partner_id_to.cooperator_type): - raise ValidationError(_("This share type could not be" - " transfered to " + - self.partner_id_to.name)) - if self.partner_id_to.is_company \ - and not self.share_product_id.by_company: - raise ValidationError(_("This share can not be" - " subscribed by a company")) - if not self.partner_id_to.is_company \ - and not self.share_product_id.by_individual: - raise ValidationError(_("This share can not be" - " subscribed an individual")) - if self.receiver_not_member and self.subscription_request \ - and not self.subscription_request.validated: - raise ValidationError(_("The information of the receiver" - " are not correct. Please correct" - " the information before" - " submitting")) - - def get_share_trans_mail_template(self): - return self.env.ref('easy_my_coop.email_template_share_transfer', - False) - - def get_share_update_mail_template(self): - return self.env.ref('easy_my_coop.email_template_share_update', - False) - - def send_share_trans_mail(self, sub_register_line): # Unused argument is used in synergie project. Do not remove. - cert_email_template = self.get_share_trans_mail_template() - cert_email_template.send_mail(self.partner_id_to.id, False) - - def send_share_update_mail(self, sub_register_line): # Unused argument is used in synergie project. Do not remove. - cert_email_template = self.get_share_update_mail_template() - cert_email_template.send_mail(self.partner_id.id, False) - - def get_subscription_register_vals(self, effective_date): - return { - 'partner_id': self.partner_id.id, 'quantity': self.quantity, - 'share_product_id': self.share_product_id.id, - 'type': self.operation_type, - 'share_unit_price': self.share_unit_price, - 'date': effective_date, - } - - @api.multi - def execute_operation(self): - self.ensure_one() - - effective_date = self.get_date_now() - sub_request = self.env['subscription.request'] - - self.validate() - - if self.state != 'approved': - raise ValidationError(_("This operation must be approved" - " before to be executed")) - - values = self.get_subscription_register_vals(effective_date) - - if self.operation_type == 'sell_back': - self.hand_share_over(self.partner_id, self.share_product_id, - self.quantity) - elif self.operation_type == 'convert': - amount_to_convert = self.share_unit_price * self.quantity - convert_quant = int(amount_to_convert / self.share_to_product_id.list_price) - remainder = amount_to_convert % self.share_to_product_id.list_price - - if convert_quant > 0 and remainder == 0: - share_ids = self.partner_id.share_ids - line = share_ids[0] - if len(share_ids) > 1: - share_ids[1:len(share_ids)].unlink() - line.write({ - 'share_number': convert_quant, - 'share_product_id': self.share_to_product_id.id, - 'share_unit_price': self.share_to_unit_price, - 'share_short_name': self.share_to_short_name - }) - values['share_to_product_id'] = self.share_to_product_id.id - values['quantity_to'] = convert_quant - else: - raise ValidationError(_("Converting just part of the" - " shares is not yet implemented")) - elif self.operation_type == 'transfer': - sequence_id = self.env.ref('easy_my_coop.sequence_subscription', False) - partner_vals = {'member': True} - if self.receiver_not_member: - partner = self.subscription_request.create_coop_partner() - # get cooperator number - sub_reg_num = int(sequence_id.next_by_id()) - partner_vals.update(sub_request.get_eater_vals( - partner, - self.share_product_id)) - partner_vals['cooperator_register_number'] = sub_reg_num - partner.write(partner_vals) - self.partner_id_to = partner - else: - # means an old member or cooperator candidate - if not self.partner_id_to.member: - if self.partner_id_to.cooperator_register_number == 0: - sub_reg_num = int(sequence_id.next_by_id()) - partner_vals['cooperator_register_number'] = sub_reg_num - partner_vals.update(sub_request.get_eater_vals( - self.partner_id_to, - self.share_product_id)) - partner_vals['old_member'] = False - self.partner_id_to.write(partner_vals) - # remove the parts to the giver - self.hand_share_over(self.partner_id, - self.share_product_id, - self.quantity) - # give the share to the receiver - self.env['share.line'].create({ - 'share_number': self.quantity, - 'partner_id': self.partner_id_to.id, - 'share_product_id': self.share_product_id.id, - 'share_unit_price': self.share_unit_price, - 'effective_date': effective_date}) - values['partner_id_to'] = self.partner_id_to.id - else: - raise ValidationError(_("This operation is not yet" - " implemented.")) - - sequence_operation = self.env.ref('easy_my_coop.sequence_register_operation', False) #noqa - sub_reg_operation = sequence_operation.next_by_id() - - values['name'] = sub_reg_operation - values['register_number_operation'] = int(sub_reg_operation) - - self.write({'state': 'done'}) - - sub_register_line = self.env['subscription.register'].create(values) - - # send mail to the receiver - if self.operation_type == 'transfer': - self.send_share_trans_mail(sub_register_line) - - self.send_share_update_mail(sub_register_line) +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from datetime import datetime + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + + +class OperationRequest(models.Model): + _name = "operation.request" + _description = "Operation request" + + def get_date_now(self): + # fixme odoo 12 uses date types + return datetime.strftime(datetime.now(), "%Y-%m-%d") + + @api.multi + @api.depends("share_product_id", "share_product_id.list_price", "quantity") + def _compute_subscription_amount(self): + for operation_request in self: + operation_request.subscription_amount = ( + operation_request.share_product_id.list_price + * operation_request.quantity + ) + + request_date = fields.Date( + string="Request date", default=lambda self: self.get_date_now() + ) + partner_id = fields.Many2one( + "res.partner", + string="Cooperator", + domain=[("member", "=", True)], + required=True, + ) + partner_id_to = fields.Many2one( + "res.partner", + string="Transfered to", + domain=[("cooperator", "=", True)], + ) + operation_type = fields.Selection( + [ + ("subscription", "Subscription"), + ("transfer", "Transfer"), + ("sell_back", "Sell Back"), + ("convert", "Conversion"), + ], + string="Operation Type", + required=True, + ) + share_product_id = fields.Many2one( + "product.product", + string="Share type", + domain=[("is_share", "=", True)], + required=True, + ) + share_to_product_id = fields.Many2one( + "product.product", + string="Convert to this share type", + domain=[("is_share", "=", True)], + ) + share_short_name = fields.Char( + related="share_product_id.short_name", string="Share type name" + ) + share_to_short_name = fields.Char( + related="share_to_product_id.short_name", string="Share to type name" + ) + share_unit_price = fields.Float( + related="share_product_id.list_price", string="Share price" + ) + share_to_unit_price = fields.Float( + related="share_to_product_id.list_price", string="Share to price" + ) + subscription_amount = fields.Float( + compute="_compute_subscription_amount", string="Operation amount" + ) + quantity = fields.Integer(string="Number of share", required=True) + state = fields.Selection( + [ + ("draft", "Draft"), + ("waiting", "Waiting"), + ("approved", "Approved"), + ("done", "Done"), + ("cancelled", "Cancelled"), + ("refused", "Refused"), + ], + string="State", + required=True, + default="draft", + ) + user_id = fields.Many2one( + "res.users", + string="Responsible", + readonly=True, + default=lambda self: self.env.user, + ) + subscription_request = fields.One2many( + "subscription.request", + "operation_request_id", + string="Share Receiver Info", + help="In case on a transfer of" + " share. If the share receiver" + " isn't a effective member then a" + " subscription form should" + " be filled.", + ) + receiver_not_member = fields.Boolean(string="Receiver is not a member") + 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(), + ) + + invoice = fields.Many2one("account.invoice", string="Invoice") + + @api.multi + def approve_operation(self): + for rec in self: + rec.write({"state": "approved"}) + + @api.multi + def refuse_operation(self): + for rec in self: + rec.write({"state": "refused"}) + + @api.multi + def submit_operation(self): + for rec in self: + rec.validate() + rec.write({"state": "waiting"}) + + @api.multi + def cancel_operation(self): + for rec in self: + rec.write({"state": "cancelled"}) + + @api.multi + def reset_to_draft(self): + for rec in self: + rec.write({"state": "draft"}) + + def get_total_share_dic(self, partner): + total_share_dic = {} + share_products = self.env["product.product"].search( + [("is_share", "=", True)] + ) + + for share_product in share_products: + total_share_dic[share_product.id] = 0 + + for line in partner.share_ids: + total_share_dic[line.share_product_id.id] += line.share_number + + return total_share_dic + + # This function doesn't handle the case of a cooperator can own + # different kinds of share type + def hand_share_over(self, partner, share_product_id, quantity): + if not partner.member: + raise ValidationError( + _( + "This operation can't be executed if the" + " cooperator is not an effective member" + ) + ) + + share_ind = len(partner.share_ids) + i = 1 + while quantity > 0: + line = self.partner_id.share_ids[share_ind - i] + if line.share_product_id.id == share_product_id.id: + if quantity > line.share_number: + quantity -= line.share_number + line.unlink() + else: + share_left = line.share_number - quantity + quantity = 0 + line.write({"share_number": share_left}) + i += 1 + # if the cooperator sold all his shares he's no more + # an effective member + remaning_share_dict = 0 + for share_quant in self.get_total_share_dic(partner).values(): + remaning_share_dict += share_quant + if remaning_share_dict == 0: + self.partner_id.write({"member": False, "old_member": True}) + + def has_share_type(self): + for line in self.partner_id.share_ids: + if line.share_product_id.id == self.share_product_id.id: + return True + return False + + def validate(self): + if not self.has_share_type() and self.operation_type in [ + "sell_back", + "transfer", + ]: + raise ValidationError( + _( + "The cooperator doesn't own this share" + " type. Please choose the appropriate" + " share type." + ) + ) + + if self.operation_type in ["sell_back", "convert", "transfer"]: + total_share_dic = self.get_total_share_dic(self.partner_id) + + if self.quantity > total_share_dic[self.share_product_id.id]: + raise ValidationError( + _( + "The cooperator can't hand over more" + " shares that he/she owns." + ) + ) + + if self.operation_type == "convert": + if self.company_id.unmix_share_type: + if self.share_product_id.code == self.share_to_product_id.code: + raise ValidationError( + _( + "You can't convert the share to" + " the same share type." + ) + ) + if self.subscription_amount != self.partner_id.total_value: + raise ValidationError( + _( + "You must convert all the shares" + " to the selected type." + ) + ) + else: + if self.subscription_amount != self.partner_id.total_value: + raise ValidationError( + _( + "Converting just part of the" + " shares is not yet implemented" + ) + ) + elif self.operation_type == "transfer": + if ( + not self.receiver_not_member + and self.company_id.unmix_share_type + and ( + self.partner_id_to.cooperator_type + and self.partner_id.cooperator_type + != self.partner_id_to.cooperator_type + ) + ): + raise ValidationError( + _( + "This share type could not be" + " transfered to " + self.partner_id_to.name + ) + ) + if ( + self.partner_id_to.is_company + and not self.share_product_id.by_company + ): + raise ValidationError( + _("This share can not be" " subscribed by a company") + ) + if ( + not self.partner_id_to.is_company + and not self.share_product_id.by_individual + ): + raise ValidationError( + _("This share can not be" " subscribed an individual") + ) + if ( + self.receiver_not_member + and self.subscription_request + and not self.subscription_request.validated + ): + raise ValidationError( + _( + "The information of the receiver" + " are not correct. Please correct" + " the information before" + " submitting" + ) + ) + + def get_share_trans_mail_template(self): + return self.env.ref( + "easy_my_coop.email_template_share_transfer", False + ) + + def get_share_update_mail_template(self): + return self.env.ref("easy_my_coop.email_template_share_update", False) + + def send_share_trans_mail( + self, sub_register_line + ): # Unused argument is used in synergie project. Do not remove. + cert_email_template = self.get_share_trans_mail_template() + cert_email_template.send_mail(self.partner_id_to.id, False) + + def send_share_update_mail( + self, sub_register_line + ): # Unused argument is used in synergie project. Do not remove. + cert_email_template = self.get_share_update_mail_template() + cert_email_template.send_mail(self.partner_id.id, False) + + def get_subscription_register_vals(self, effective_date): + return { + "partner_id": self.partner_id.id, + "quantity": self.quantity, + "share_product_id": self.share_product_id.id, + "type": self.operation_type, + "share_unit_price": self.share_unit_price, + "date": effective_date, + } + + @api.multi + def execute_operation(self): + self.ensure_one() + + effective_date = self.get_date_now() + sub_request = self.env["subscription.request"] + + self.validate() + + if self.state != "approved": + raise ValidationError( + _("This operation must be approved" " before to be executed") + ) + + values = self.get_subscription_register_vals(effective_date) + + if self.operation_type == "sell_back": + self.hand_share_over( + self.partner_id, self.share_product_id, self.quantity + ) + elif self.operation_type == "convert": + amount_to_convert = self.share_unit_price * self.quantity + convert_quant = int( + amount_to_convert / self.share_to_product_id.list_price + ) + remainder = amount_to_convert % self.share_to_product_id.list_price + + if convert_quant > 0 and remainder == 0: + share_ids = self.partner_id.share_ids + line = share_ids[0] + if len(share_ids) > 1: + share_ids[1 : len(share_ids)].unlink() + line.write( + { + "share_number": convert_quant, + "share_product_id": self.share_to_product_id.id, + "share_unit_price": self.share_to_unit_price, + "share_short_name": self.share_to_short_name, + } + ) + values["share_to_product_id"] = self.share_to_product_id.id + values["quantity_to"] = convert_quant + else: + raise ValidationError( + _( + "Converting just part of the" + " shares is not yet implemented" + ) + ) + elif self.operation_type == "transfer": + sequence_id = self.env.ref( + "easy_my_coop.sequence_subscription", False + ) + partner_vals = {"member": True} + if self.receiver_not_member: + partner = self.subscription_request.create_coop_partner() + # get cooperator number + sub_reg_num = int(sequence_id.next_by_id()) + partner_vals.update(sub_request.get_eater_vals( + partner, + self.share_product_id)) + partner_vals['cooperator_register_number'] = sub_reg_num + partner.write(partner_vals) + self.partner_id_to = partner + else: + # means an old member or cooperator candidate + if not self.partner_id_to.member: + if self.partner_id_to.cooperator_register_number == 0: + sub_reg_num = int(sequence_id.next_by_id()) + partner_vals['cooperator_register_number'] = sub_reg_num + partner_vals.update(sub_request.get_eater_vals( + self.partner_id_to, + self.share_product_id)) + partner_vals['old_member'] = False + self.partner_id_to.write(partner_vals) + # remove the parts to the giver + self.hand_share_over( + self.partner_id, self.share_product_id, self.quantity + ) + # give the share to the receiver + self.env["share.line"].create( + { + "share_number": self.quantity, + "partner_id": self.partner_id_to.id, + "share_product_id": self.share_product_id.id, + "share_unit_price": self.share_unit_price, + "effective_date": effective_date, + } + ) + values["partner_id_to"] = self.partner_id_to.id + else: + raise ValidationError( + _("This operation is not yet" " implemented.") + ) + + sequence_operation = self.env.ref( + "easy_my_coop.sequence_register_operation", False + ) # noqa + sub_reg_operation = sequence_operation.next_by_id() + + values["name"] = sub_reg_operation + values["register_number_operation"] = int(sub_reg_operation) + + self.write({"state": "done"}) + + sub_register_line = self.env["subscription.register"].create(values) + + # send mail to the receiver + if self.operation_type == "transfer": + self.send_share_trans_mail(sub_register_line) + + self.send_share_update_mail(sub_register_line) diff --git a/easy_my_coop/models/partner.py b/easy_my_coop/models/partner.py index f6d9afc..613b04c 100644 --- a/easy_my_coop/models/partner.py +++ b/easy_my_coop/models/partner.py @@ -7,7 +7,7 @@ from odoo import api, fields, models class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" @api.multi def _get_report_base_filename(self): @@ -15,11 +15,11 @@ class ResPartner(models.Model): if self.member: return "Cooperator Certificate - %s" % self.name else: - return 'unknown' + return "unknown" @api.multi def _invoice_total(self): - account_invoice_report = self.env['account.invoice.report'] + account_invoice_report = self.env["account.invoice.report"] if not self.ids: self.total_invoiced = 0.0 return True @@ -28,7 +28,9 @@ class ResPartner(models.Model): all_partner_ids = [] for partner in self: # price_total is in the company currency - all_partners_and_children[partner] = self.search([('id', 'child_of', partner.id)]).ids + all_partners_and_children[partner] = self.search( + [("id", "child_of", partner.id)] + ).ids all_partner_ids += all_partners_and_children[partner] # searching account.invoice.report via the orm is comparatively @@ -38,30 +40,39 @@ class ResPartner(models.Model): # the user's company access directly these elements # generate where clause to include multicompany rules - where_query = account_invoice_report._where_calc([ - ('partner_id', 'in', all_partner_ids), - ('state', 'not in', ['draft', 'cancel']), - ('company_id', '=', self.env.user.company_id.id), - ('type', 'in', ('out_invoice', 'out_refund')), - ('release_capital_request', '=', False), - ]) - account_invoice_report._apply_ir_rules(where_query, 'read') + where_query = account_invoice_report._where_calc( + [ + ("partner_id", "in", all_partner_ids), + ("state", "not in", ["draft", "cancel"]), + ("company_id", "=", self.env.user.company_id.id), + ("type", "in", ("out_invoice", "out_refund")), + ("release_capital_request", "=", False), + ] + ) + account_invoice_report._apply_ir_rules(where_query, "read") from_clause, where_clause, where_clause_params = where_query.get_sql() # price_total is in the company currency - query = """ + query = ( + """ SELECT SUM(price_total) as total, partner_id FROM account_invoice_report account_invoice_report WHERE %s GROUP BY partner_id - """ % where_clause + """ + % where_clause + ) self.env.cr.execute(query, where_clause_params) price_totals = self.env.cr.dictfetchall() for partner, child_ids in all_partners_and_children.items(): - partner.total_invoiced = sum(price['total'] for price in price_totals if price['partner_id'] in child_ids) + partner.total_invoiced = sum( + price["total"] + for price in price_totals + if price["partner_id"] in child_ids + ) @api.multi - @api.depends('share_ids') + @api.depends("share_ids") def _compute_effective_date(self): # TODO change it to compute it from the share register for partner in self: @@ -70,22 +81,20 @@ class ResPartner(models.Model): @api.multi def _get_share_type(self): - shares = ( - self.env['product.product'] - .search([('is_share', '=', True)]) - ) - share_types = [ - (share.default_code, share.short_name) for share in shares - ] - return [('', '')] + share_types + shares = self.env["product.product"].search([("is_share", "=", True)]) + share_types = [(s.default_code, s.short_name) for s in shares] + return [("", "")] + share_types @api.multi - @api.depends('share_ids', 'share_ids.share_product_id', - 'share_ids.share_product_id.default_code', - 'share_ids.share_number') + @api.depends( + "share_ids", + "share_ids.share_product_id", + "share_ids.share_product_id.default_code", + "share_ids.share_number", + ) def _compute_cooperator_type(self): for partner in self: - share_type = '' + share_type = "" for line in partner.share_ids: if line.share_number > 0: share_type = line.share_product_id.default_code @@ -93,7 +102,7 @@ class ResPartner(models.Model): partner.cooperator_type = share_type @api.multi - @api.depends('share_ids') + @api.depends("share_ids") def _compute_share_info(self): for partner in self: number_of_share = 0 @@ -104,62 +113,75 @@ class ResPartner(models.Model): partner.number_of_share = number_of_share partner.total_value = total_value - cooperator = fields.Boolean(string='Cooperator', - help="Check this box if this contact is a" - " cooperator (effective or not).") - member = fields.Boolean(string='Effective cooperator', - help="Check this box if this cooperator" - " is an effective member.") - coop_candidate = fields.Boolean(string="Cooperator candidate", - compute="_compute_coop_candidate", - store=True, - readonly=True) - old_member = fields.Boolean(string='Old cooperator', - help="Check this box if this cooperator is" - " no more an effective member.") + cooperator = fields.Boolean( + string="Cooperator", + help="Check this box if this contact is a" + " cooperator (effective or not).", + ) + member = fields.Boolean( + string="Effective cooperator", + help="Check this box if this cooperator" " is an effective member.", + ) + coop_candidate = fields.Boolean( + string="Cooperator candidate", + compute="_compute_coop_candidate", + store=True, + readonly=True, + ) + old_member = fields.Boolean( + string="Old cooperator", + help="Check this box if this cooperator is" + " no more an effective member.", + ) # todo use oca partner_contact_gender - gender = fields.Selection([('male', 'Male'), - ('female', 'Female'), - ('other', 'Other')], - string='Gender') - share_ids = fields.One2many('share.line', - 'partner_id', - string='Share Lines') - cooperator_register_number = fields.Integer(string='Cooperator Number') - number_of_share = fields.Integer(compute="_compute_share_info", - multi='share', - string='Number of share', - readonly=True) - total_value = fields.Float(compute="_compute_share_info", - multi='share', - string='Total value of shares', - readonly=True) - company_register_number = fields.Char(string='Company Register Number') - cooperator_type = fields.Selection(selection='_get_share_type', - compute=_compute_cooperator_type, - string='Cooperator Type', - store=True) - effective_date = fields.Date(sting="Effective Date", - compute=_compute_effective_date, - store=True) + gender = fields.Selection( + [("male", "Male"), ("female", "Female"), ("other", "Other")], + string="Gender", + ) + share_ids = fields.One2many( + "share.line", "partner_id", string="Share Lines" + ) + cooperator_register_number = fields.Integer(string="Cooperator Number") + number_of_share = fields.Integer( + compute="_compute_share_info", + multi="share", + string="Number of share", + readonly=True, + ) + total_value = fields.Float( + compute="_compute_share_info", + multi="share", + string="Total value of shares", + readonly=True, + ) + company_register_number = fields.Char(string="Company Register Number") + cooperator_type = fields.Selection( + selection="_get_share_type", + compute=_compute_cooperator_type, + string="Cooperator Type", + store=True, + ) + effective_date = fields.Date( + sting="Effective Date", compute=_compute_effective_date, store=True + ) representative = fields.Boolean(string="Legal Representative") - subscription_request_ids = fields.One2many('subscription.request', - 'partner_id', - string="Subscription request") - legal_form = fields.Selection([('', '')], - string="Legal form") + subscription_request_ids = fields.One2many( + "subscription.request", "partner_id", string="Subscription request" + ) + legal_form = fields.Selection([("", "")], string="Legal form") data_policy_approved = fields.Boolean(string="Approved Data Policy") internal_rules_approved = fields.Boolean(string="Approved Internal Rules") @api.multi - @api.depends('subscription_request_ids.state') + @api.depends("subscription_request_ids.state") def _compute_coop_candidate(self): for partner in self: if partner.member: is_candidate = False else: sub_requests = partner.subscription_request_ids.filtered( - lambda record: record.state == 'done') + lambda record: record.state == "done" + ) is_candidate = bool(sub_requests) partner.coop_candidate = is_candidate @@ -167,19 +189,24 @@ class ResPartner(models.Model): @api.multi def has_representative(self): self.ensure_one() - if self.child_ids.filtered('representative'): + if self.child_ids.filtered("representative"): return True return False @api.multi def get_representative(self): self.ensure_one() - return self.child_ids.filtered('representative') + return self.child_ids.filtered("representative") def get_cooperator_from_email(self, email): - return self.env['res.partner'].search([('cooperator', '=', True), - ('email', '=', email)]) + return self.env["res.partner"].search( + [("cooperator", "=", True), ("email", "=", email)] + ) def get_cooperator_from_crn(self, company_register_number): - return self.env['res.partner'].search([('cooperator', '=', True), - ('company_register_number', '=', company_register_number)]) + return self.env["res.partner"].search( + [ + ("cooperator", "=", True), + ("company_register_number", "=", company_register_number), + ] + ) diff --git a/easy_my_coop/models/product.py b/easy_my_coop/models/product.py index 0706a1d..af035e8 100644 --- a/easy_my_coop/models/product.py +++ b/easy_my_coop/models/product.py @@ -1,37 +1,41 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import api, fields, models - - -class ProductTemplate(models.Model): - _inherit = 'product.template' - - is_share = fields.Boolean(string="Is share?") - short_name = fields.Char(string="Short name") - display_on_website = fields.Boolean(string="Display on website") - default_share_product = fields.Boolean(string="Default share product") - minimum_quantity = fields.Integer(string="Minimum quantity", default=1) - force_min_qty = fields.Boolean(String="Force minimum quantity?") - by_company = fields.Boolean(string="Can be subscribed by companies?") - by_individual = fields.Boolean(string="Can be subscribed by individuals?") - customer = fields.Boolean(string="Become customer") - mail_template = fields.Many2one('mail.template', - string="Mail template") - - @api.multi - def get_web_share_products(self, is_company): - if is_company is True: - product_templates = self.env['product.template'].search([ - ('is_share', '=', True), - ('display_on_website', '=', True), - ('by_company', '=', True)]) - else: - product_templates = self.env['product.template'].search([ - ('is_share', '=', True), - ('display_on_website', '=', True), - ('by_individual', '=', True)]) - return product_templates +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import api, fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_share = fields.Boolean(string="Is share?") + short_name = fields.Char(string="Short name") + display_on_website = fields.Boolean(string="Display on website") + default_share_product = fields.Boolean(string="Default share product") + minimum_quantity = fields.Integer(string="Minimum quantity", default=1) + force_min_qty = fields.Boolean(String="Force minimum quantity?") + by_company = fields.Boolean(string="Can be subscribed by companies?") + by_individual = fields.Boolean(string="Can be subscribed by individuals?") + customer = fields.Boolean(string="Become customer") + mail_template = fields.Many2one("mail.template", string="Mail template") + + @api.multi + def get_web_share_products(self, is_company): + if is_company is True: + product_templates = self.env["product.template"].search( + [ + ("is_share", "=", True), + ("display_on_website", "=", True), + ("by_company", "=", True), + ] + ) + else: + product_templates = self.env["product.template"].search( + [ + ("is_share", "=", True), + ("display_on_website", "=", True), + ("by_individual", "=", True), + ] + ) + return product_templates diff --git a/easy_my_coop/models/res_partner_bank.py b/easy_my_coop/models/res_partner_bank.py index 53e2bb5..6e845d3 100644 --- a/easy_my_coop/models/res_partner_bank.py +++ b/easy_my_coop/models/res_partner_bank.py @@ -1,15 +1,14 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Coop IT Easy SCRL fs -# Houssine Bakkali -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - - -from odoo import models - - -class ResPartnerBank(models.Model): - _inherit = 'res.partner.bank' - - _sql_constraints = [ - ('unique_number', 'Check(1=1)', 'Account Number must be unique!'), - ] +# Copyright 2019 Coop IT Easy SCRL fs +# Houssine Bakkali +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + + +from odoo import models + + +class ResPartnerBank(models.Model): + _inherit = "res.partner.bank" + + _sql_constraints = [ + ("unique_number", "Check(1=1)", "Account Number must be unique!") + ] diff --git a/easy_my_coop/report/account_invoice_report.py b/easy_my_coop/report/account_invoice_report.py index bd1fb90..3374ce0 100644 --- a/easy_my_coop/report/account_invoice_report.py +++ b/easy_my_coop/report/account_invoice_report.py @@ -1,13 +1,19 @@ -from odoo import fields, models - - -class AccountInvoiceReport(models.Model): - _inherit = "account.invoice.report" - - release_capital_request = fields.Boolean(string="Release capital request") - - def _select(self): - return super(AccountInvoiceReport, self)._select() + ", sub.release_capital_request as release_capital_request" - - def _sub_select(self): - return super(AccountInvoiceReport, self)._sub_select() + ", ai.release_capital_request as release_capital_request" +from odoo import fields, models + + +class AccountInvoiceReport(models.Model): + _inherit = "account.invoice.report" + + release_capital_request = fields.Boolean(string="Release capital request") + + def _select(self): + return ( + super(AccountInvoiceReport, self)._select() + + ", sub.release_capital_request as release_capital_request" + ) + + def _sub_select(self): + return ( + super(AccountInvoiceReport, self)._sub_select() + + ", ai.release_capital_request as release_capital_request" + ) diff --git a/easy_my_coop/wizard/account_invoice_refund.py b/easy_my_coop/wizard/account_invoice_refund.py index 097d764..f88ee42 100644 --- a/easy_my_coop/wizard/account_invoice_refund.py +++ b/easy_my_coop/wizard/account_invoice_refund.py @@ -6,14 +6,14 @@ class AccountInvoiceRefund(models.TransientModel): _inherit = "account.invoice.refund" @api.multi - def compute_refund(self, mode='refund'): + def compute_refund(self, mode="refund"): result = super(AccountInvoiceRefund, self).compute_refund(mode) context = dict(self._context or {}) - inv = self.env['account.invoice'].browse(context.get('active_ids')) + inv = self.env["account.invoice"].browse(context.get("active_ids")) if inv.release_capital_request: - domain = result['domain'] - t = ('release_capital_request', '=', True) + domain = result["domain"] + t = ("release_capital_request", "=", True) out = [t if e[0] == t[0] else e for e in domain] - result['domain'] = out + result["domain"] = out return result diff --git a/easy_my_coop/wizard/create_subscription_from_partner.py b/easy_my_coop/wizard/create_subscription_from_partner.py index ba418e3..def7896 100644 --- a/easy_my_coop/wizard/create_subscription_from_partner.py +++ b/easy_my_coop/wizard/create_subscription_from_partner.py @@ -1,36 +1,40 @@ -from odoo import api, fields, models, _ -import odoo.addons.decimal_precision as dp +from odoo import _, api, fields, models from odoo.exceptions import UserError +import odoo.addons.decimal_precision as dp + class PartnerCreateSubscription(models.TransientModel): _name = "partner.create.subscription" _description = "Create Subscription From Partner" @api.multi - @api.onchange('share_product') + @api.onchange("share_product") def on_change_share_type(self): self.share_qty = self.share_product.minimum_quantity @api.model def _default_product_id(self): - domain = [('is_share', '=', True), - ('default_share_product', '=', True)] - active_id = self.env.context.get('active_id') + domain = [ + ("is_share", "=", True), + ("default_share_product", "=", True), + ] + active_id = self.env.context.get("active_id") if active_id: - partner = self.env['res.partner'].browse(active_id) + partner = self.env["res.partner"].browse(active_id) if partner.is_company: - domain.append(('by_company', '=', True)) + domain.append(("by_company", "=", True)) else: - domain.append(('by_individual', '=', True)) + domain.append(("by_individual", "=", True)) - return self.env['product.product'].search(domain)[0] + return self.env["product.product"].search(domain)[0] def _get_representative(self): partner = self._get_partner() if partner.is_company: - return partner.search([('parent_id', '=', partner.id), - ('representative', '=', True)]) + return partner.search( + [("parent_id", "=", partner.id), ("representative", "=", True)] + ) return False @api.model @@ -49,8 +53,8 @@ class PartnerCreateSubscription(models.TransientModel): @api.model def _get_partner(self): - active_id = self.env.context.get('active_id') - return self.env['res.partner'].browse(active_id) + active_id = self.env.context.get("active_id") + return self.env["res.partner"].browse(active_id) @api.model def _get_is_company(self): @@ -75,124 +79,140 @@ class PartnerCreateSubscription(models.TransientModel): @api.model def _get_possible_share(self): - domain = [('is_share', '=', True)] + domain = [("is_share", "=", True)] partner = self._get_partner() if partner.is_company: - domain.append(('by_company', '=', True)) + domain.append(("by_company", "=", True)) else: - domain.append(('by_individual', '=', True)) + domain.append(("by_individual", "=", True)) return domain @api.multi - @api.depends('share_product', 'share_qty') + @api.depends("share_product", "share_qty") def _compute_subscription_amount(self): for sub_request in self: - sub_request.subscription_amount = (sub_request. - share_product.list_price * - sub_request.share_qty) - - is_company = fields.Boolean(String="Is company?", - default=_get_is_company) - cooperator = fields.Many2one('res.partner', - string="Cooperator", - default=_get_partner) - register_number = fields.Char(string="Register Company Number", - default=_get_register_number) - email = fields.Char(string="Email", - required=True, - default=_get_email) - bank_account = fields.Char(string="Bank account", - required=True, - default=_get_bank_account) - share_product = fields.Many2one('product.product', - string='Share Type', - domain=_get_possible_share, - default=_default_product_id, - required=True) - share_qty = fields.Integer(string="Share Quantity", - required=True) - share_unit_price = fields.Float(related='share_product.list_price', - string='Share price', - readonly=True) - subscription_amount = fields.Float(compute='_compute_subscription_amount', - string='Subscription amount', - digits=dp.get_precision('Account'), - readonly=True) - representative_name = fields.Char(string='Representative name', - default=_get_representative_name) - representative_email = fields.Char(string='Representative email', - default=_get_representative_email) + sub_request.subscription_amount = ( + sub_request.share_product.list_price * sub_request.share_qty + ) + + is_company = fields.Boolean(String="Is company?", default=_get_is_company) + cooperator = fields.Many2one( + "res.partner", string="Cooperator", default=_get_partner + ) + register_number = fields.Char( + string="Register Company Number", default=_get_register_number + ) + email = fields.Char(string="Email", required=True, default=_get_email) + bank_account = fields.Char( + string="Bank account", required=True, default=_get_bank_account + ) + share_product = fields.Many2one( + "product.product", + string="Share Type", + domain=_get_possible_share, + default=_default_product_id, + required=True, + ) + share_qty = fields.Integer(string="Share Quantity", required=True) + share_unit_price = fields.Float( + related="share_product.list_price", string="Share price", readonly=True + ) + subscription_amount = fields.Float( + compute="_compute_subscription_amount", + string="Subscription amount", + digits=dp.get_precision("Account"), + readonly=True, + ) + representative_name = fields.Char( + string="Representative name", default=_get_representative_name + ) + representative_email = fields.Char( + string="Representative email", default=_get_representative_email + ) @api.multi def create_subscription(self): - sub_req = self.env['subscription.request'] - partner_obj = self.env['res.partner'] + sub_req = self.env["subscription.request"] + partner_obj = self.env["res.partner"] cooperator = self.cooperator - vals = {'partner_id': cooperator.id, - 'share_product_id': self.share_product.id, - 'ordered_parts': self.share_qty, - 'cooperator': True, - 'user_id': self.env.uid, - 'email': self.email, - 'source': 'crm', - 'address': self.cooperator.street, - 'zip_code': self.cooperator.zip, - 'city': self.cooperator.city, - 'country_id': self.cooperator.country_id.id - } + vals = { + "partner_id": cooperator.id, + "share_product_id": self.share_product.id, + "ordered_parts": self.share_qty, + "cooperator": True, + "user_id": self.env.uid, + "email": self.email, + "source": "crm", + "address": self.cooperator.street, + "zip_code": self.cooperator.zip, + "city": self.cooperator.city, + "country_id": self.cooperator.country_id.id, + } if self.is_company: - vals['company_name'] = cooperator.name - vals['company_email'] = cooperator.email - vals['name'] = '/' - vals['company_register_number'] = self.register_number - vals['is_company'] = True + vals["company_name"] = cooperator.name + vals["company_email"] = cooperator.email + vals["name"] = "/" + vals["company_register_number"] = self.register_number + vals["is_company"] = True else: - vals['name'] = cooperator.name + vals["name"] = cooperator.name coop_vals = {} if not self._get_email(): - coop_vals['email'] = self.email + coop_vals["email"] = self.email if not self._get_register_number(): if self.is_company: - coop_vals['company_register_number'] = self.register_number + coop_vals["company_register_number"] = self.register_number if self.is_company and not self._get_representative(): representative = False if self.representative_email: representative = partner_obj.search( - [('email', '=', self.representative_email)]) + [("email", "=", self.representative_email)] + ) if representative: if len(representative) > 1: - raise UserError(_('There is two different persons with ' - 'the same national register number. ' - 'Please proceed to a merge before to ' - 'continue')) + raise UserError( + _( + "There is two different persons with " + "the same national register number. " + "Please proceed to a merge before to " + "continue" + ) + ) if representative.parent_id: - raise UserError(_("A person can't be representative of " - "two different companies.")) + raise UserError( + _( + "A person can't be representative of " + "two different companies." + ) + ) representative.parent_id = cooperator.id else: if self.representative_email: - represent_vals = {'name': self.representative_name, - 'cooperator': True, - 'email': self.representative_email, - 'parent_id': cooperator.id, - 'representative': True} + represent_vals = { + "name": self.representative_name, + "cooperator": True, + "email": self.representative_email, + "parent_id": cooperator.id, + "representative": True, + } partner_obj.create(represent_vals) if not self._get_bank_account(): - partner_bank = self.env['res.partner.bank'] - partner_bank.create({'partner_id': cooperator.id, - 'acc_number': self.bank_account}) - vals['iban'] = self.bank_account + partner_bank = self.env["res.partner.bank"] + partner_bank.create( + {"partner_id": cooperator.id, "acc_number": self.bank_account} + ) + vals["iban"] = self.bank_account if self.is_company: representative = self._get_representative() - vals['name'] = representative.name + vals["name"] = representative.name if coop_vals: cooperator.write(coop_vals) @@ -200,10 +220,10 @@ class PartnerCreateSubscription(models.TransientModel): new_sub_req = sub_req.create(vals) return { - 'type': 'ir.actions.act_window', - 'view_type': 'form, tree', - 'view_mode': 'form', - 'res_model': 'subscription.request', - 'res_id': new_sub_req.id, - 'target': 'current', + "type": "ir.actions.act_window", + "view_type": "form, tree", + "view_mode": "form", + "res_model": "subscription.request", + "res_id": new_sub_req.id, + "target": "current", } diff --git a/easy_my_coop/wizard/update_partner_info.py b/easy_my_coop/wizard/update_partner_info.py index d35920a..09452ae 100644 --- a/easy_my_coop/wizard/update_partner_info.py +++ b/easy_my_coop/wizard/update_partner_info.py @@ -7,8 +7,8 @@ class PartnerUpdateInfo(models.TransientModel): @api.model def _get_partner(self): - active_id = self.env.context.get('active_id') - return self.env['res.partner'].browse(active_id) + active_id = self.env.context.get("active_id") + return self.env["res.partner"].browse(active_id) @api.model def _get_register_number(self): @@ -20,13 +20,13 @@ class PartnerUpdateInfo(models.TransientModel): def _get_is_company(self): return self._get_partner().is_company - is_company = fields.Boolean(string="Is company", - default=_get_is_company) - register_number = fields.Char(string="Register Company Number", - default=_get_register_number) - cooperator = fields.Many2one('res.partner', - string="Cooperator", - default=_get_partner) + is_company = fields.Boolean(string="Is company", default=_get_is_company) + register_number = fields.Char( + string="Register Company Number", default=_get_register_number + ) + cooperator = fields.Many2one( + "res.partner", string="Cooperator", default=_get_partner + ) all = fields.Boolean(string="Update from subscription request") birthdate = fields.Boolean(string="Set missing birth date") legal_form = fields.Boolean(string="Set legal form") @@ -34,41 +34,50 @@ class PartnerUpdateInfo(models.TransientModel): @api.multi def update(self): - partner_obj = self.env['res.partner'] + partner_obj = self.env["res.partner"] cooperator = self.cooperator coop_vals = {} if self.all: if self.legal_form or self.representative_function: - coops = partner_obj.search([('cooperator', '=', True), - ('is_company', '=', True)]) + coops = partner_obj.search( + [("cooperator", "=", True), ("is_company", "=", True)] + ) for coop in coops: coop_vals = {} - sub_reqs = coop.subscription_request_ids.filtered(lambda r: r.state in ['done', 'paid']) + sub_reqs = coop.subscription_request_ids.filtered( + lambda r: r.state in ["done", "paid"] + ) if sub_reqs: sub_req = sub_reqs[0] if self.legal_form: - coop_vals['legal_form'] = sub_req.company_type + coop_vals["legal_form"] = sub_req.company_type coop.write(coop_vals) if self.representative_function: contact = coop.get_representative() contact.function = sub_req.contact_person_function else: - coops = partner_obj.search([('cooperator', '=', True), - ('birthdate_date', '=', False), - ('is_company', '=', False)]) + coops = partner_obj.search( + [ + ("cooperator", "=", True), + ("birthdate_date", "=", False), + ("is_company", "=", False), + ] + ) for coop in coops: coop_vals = {} - sub_reqs = coop.subscription_request_ids.filtered(lambda r: r.state in ['done', 'paid']) + sub_reqs = coop.subscription_request_ids.filtered( + lambda r: r.state in ["done", "paid"] + ) if sub_reqs: sub_req = sub_reqs[0] if self.birthdate: - coop_vals['birthdate_date'] = sub_req.birthdate + coop_vals["birthdate_date"] = sub_req.birthdate coop.write(coop_vals) else: if cooperator: if cooperator.is_company: - coop_vals['company_register_number'] = self.register_number + coop_vals["company_register_number"] = self.register_number if coop_vals: cooperator.write(coop_vals) diff --git a/easy_my_coop/wizard/update_share_line.py b/easy_my_coop/wizard/update_share_line.py index f48ec58..8aa7604 100644 --- a/easy_my_coop/wizard/update_share_line.py +++ b/easy_my_coop/wizard/update_share_line.py @@ -1,4 +1,4 @@ -from odoo import api, fields, models, _ +from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -8,8 +8,8 @@ class ShareLineUpdateInfo(models.TransientModel): @api.model def _get_share_line(self): - active_id = self.env.context.get('active_id') - return self.env['share.line'].browse(active_id) + active_id = self.env.context.get("active_id") + return self.env["share.line"].browse(active_id) @api.model def _get_effective_date(self): @@ -17,14 +17,15 @@ class ShareLineUpdateInfo(models.TransientModel): return share_line.effective_date - effective_date = fields.Date(string="effective date", - required=True, - default=_get_effective_date) - cooperator = fields.Many2one(related='share_line.partner_id', - string="Cooperator") - share_line = fields.Many2one('share.line', - string="Share line", - default=_get_share_line) + effective_date = fields.Date( + string="effective date", required=True, default=_get_effective_date + ) + cooperator = fields.Many2one( + related="share_line.partner_id", string="Cooperator" + ) + share_line = fields.Many2one( + "share.line", string="Share line", default=_get_share_line + ) @api.multi def update(self): @@ -32,15 +33,22 @@ class ShareLineUpdateInfo(models.TransientModel): line = self.share_line cooperator = line.partner_id - sub_reg = self.env['subscription.register'].search( - [('partner_id', '=', cooperator.id), - ('share_product_id', '=', line.share_product_id.id), - ('quantity', '=', line.share_number), - ('date', '=', line.effective_date)]) + sub_reg = self.env["subscription.register"].search( + [ + ("partner_id", "=", cooperator.id), + ("share_product_id", "=", line.share_product_id.id), + ("quantity", "=", line.share_number), + ("date", "=", line.effective_date), + ] + ) if sub_reg: if len(sub_reg) > 1: - raise UserError(_("Error the update return more than one" - " subscription register lines.")) + raise UserError( + _( + "Error the update return more than one" + " subscription register lines." + ) + ) else: line.effective_date = self.effective_date sub_reg.date = self.effective_date diff --git a/easy_my_coop/wizard/validate_subscription_request.py b/easy_my_coop/wizard/validate_subscription_request.py index 21131de..8193ca7 100644 --- a/easy_my_coop/wizard/validate_subscription_request.py +++ b/easy_my_coop/wizard/validate_subscription_request.py @@ -7,10 +7,12 @@ class ValidateSubscriptionRequest(models.TransientModel): @api.multi def validate(self): - selected_requests = self.env['subscription.request'].browse( - self._context.get('active_ids')) + selected_requests = self.env["subscription.request"].browse( + self._context.get("active_ids") + ) subscription_requests = selected_requests.filtered( - lambda record: record.state in ['draft', 'waiting']) + lambda record: record.state in ["draft", "waiting"] + ) for subscription_request in subscription_requests: subscription_request.validate_subscription_request() diff --git a/easy_my_coop_be/__manifest__.py b/easy_my_coop_be/__manifest__.py index 2a2087d..5f735ef 100644 --- a/easy_my_coop_be/__manifest__.py +++ b/easy_my_coop_be/__manifest__.py @@ -3,20 +3,15 @@ # - Houssine BAKKALI - # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).# { - "name": "Easy My Coop Be", + "name": "Easy My Coop Belgium", + "summary": "Easy My Coop Belgium Localization", "version": "12.0.1.2.0", - "depends": ["easy_my_coop", - "l10n_be", - "l10n_be_invoice_bba", - ], - "author": "Houssine BAKKALI ", + "depends": ["easy_my_coop", "l10n_be", "l10n_be_invoice_bba"], + "author": "Coop IT Easy SCRLfs", "category": "Cooperative management", "website": "www.coopiteasy.be", "license": "AGPL-3", - "description": """ - This is the belgian localization for the easy my coop module. - """, - 'data': [], - 'installable': True, - 'application': False, + "data": [], + "installable": True, + "application": False, } diff --git a/easy_my_coop_be/models/coop.py b/easy_my_coop_be/models/coop.py index 5fd2782..f4237a4 100644 --- a/easy_my_coop_be/models/coop.py +++ b/easy_my_coop_be/models/coop.py @@ -1,27 +1,26 @@ -from odoo import fields, models - - -class SubscriptionRequest(models.Model): - _inherit = 'subscription.request' - - company_type = fields.Selection([('scrl', 'SCRL'), - ('asbl', 'ASBL'), - ('sprl', 'SPRL'), - ('sa', 'SA')]) - - def get_partner_company_vals(self): - vals = super(SubscriptionRequest, self).get_partner_company_vals() - vals['out_inv_comm_algorithm'] = 'random' - return vals - - def get_partner_vals(self): - vals = super(SubscriptionRequest, self).get_partner_vals() - vals['out_inv_comm_type'] = 'bba' - vals['out_inv_comm_algorithm'] = 'random' - return vals - - def get_representative_valst(self): - vals = super(SubscriptionRequest, self).get_representative_vals() - vals['out_inv_comm_type'] = 'bba' - vals['out_inv_comm_algorithm'] = 'random' - return vals +from odoo import fields, models + + +class SubscriptionRequest(models.Model): + _inherit = "subscription.request" + + company_type = fields.Selection( + [("scrl", "SCRL"), ("asbl", "ASBL"), ("sprl", "SPRL"), ("sa", "SA")] + ) + + def get_partner_company_vals(self): + vals = super(SubscriptionRequest, self).get_partner_company_vals() + vals["out_inv_comm_algorithm"] = "random" + return vals + + def get_partner_vals(self): + vals = super(SubscriptionRequest, self).get_partner_vals() + vals["out_inv_comm_type"] = "bba" + vals["out_inv_comm_algorithm"] = "random" + return vals + + def get_representative_valst(self): + vals = super(SubscriptionRequest, self).get_representative_vals() + vals["out_inv_comm_type"] = "bba" + vals["out_inv_comm_algorithm"] = "random" + return vals diff --git a/easy_my_coop_be/models/partner.py b/easy_my_coop_be/models/partner.py index d42a178..48e0992 100644 --- a/easy_my_coop_be/models/partner.py +++ b/easy_my_coop_be/models/partner.py @@ -1,10 +1,14 @@ -from odoo import fields, models - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - legal_form = fields.Selection(selection_add=[('scrl', 'SCRL'), - ('asbl', 'ASBL'), - ('sprl', 'SPRL'), - ('sa', 'SA')]) +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + legal_form = fields.Selection( + selection_add=[ + ("scrl", "SCRL"), + ("asbl", "ASBL"), + ("sprl", "SPRL"), + ("sa", "SA"), + ] + ) diff --git a/easy_my_coop_ch/__manifest__.py b/easy_my_coop_ch/__manifest__.py index 0cb5f23..398e470 100644 --- a/easy_my_coop_ch/__manifest__.py +++ b/easy_my_coop_ch/__manifest__.py @@ -1,37 +1,19 @@ -# -*- 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 . -# -############################################################################## +# Copyright 2013-2018 Open Architects Consulting SPRL. +# Copyright 2018 Coop IT Easy SCRLfs () +# - Houssine BAKKALI - +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - "name": "Easy My Coop CH", + "name": "Easy My Coop Switzerland", + "summary": "Easy My Coop Switzerland localization", "version": "12.0.1.0.2", - "depends": ["easy_my_coop", - "l10n_ch"], - "author": "Houssine BAKKALI ", + "license": "AGPL-3", + "depends": ["easy_my_coop", "l10n_ch"], + "author": "Coop IT Easy SCRLfs", "category": "Cooperative management", - 'webstite': "www.coopiteasy.be", - "description": """ - This is the swiss localization for the easy my coop module - """, - 'data': [ - 'views/subscription_template.xml', - 'views/subscription_request_view.xml' + "webstite": "www.coopiteasy.be", + "data": [ + "views/subscription_template.xml", + "views/subscription_request_view.xml", ], - 'installable': True, + "installable": True, } diff --git a/easy_my_coop_ch/models/coop.py b/easy_my_coop_ch/models/coop.py index bdc9384..cb30d7a 100644 --- a/easy_my_coop_ch/models/coop.py +++ b/easy_my_coop_ch/models/coop.py @@ -1,22 +1,25 @@ -from odoo import fields, models - - -class SubscriptionRequest(models.Model): - _inherit = 'subscription.request' - - company_type = fields.Selection(selection_add=[ - ('ei', 'Individual company'), - ('snc', 'Partnership'), - ('sa', 'Limited company (SA)'), - ('sarl', 'Limited liability company (Ltd)'), #noqa - ('sc', 'Cooperative'), - ('asso', 'Association'), - ('fond', 'Foundation'), - ('edp', 'Company under public law')]) - - def get_required_field(self): - req_fields = super(SubscriptionRequest, self).get_required_field() - if 'iban' in req_fields: - req_fields.remove('iban') - - return req_fields +from odoo import fields, models + + +class SubscriptionRequest(models.Model): + _inherit = "subscription.request" + + company_type = fields.Selection( + selection_add=[ + ("ei", "Individual company"), + ("snc", "Partnership"), + ("sa", "Limited company (SA)"), + ("sarl", "Limited liability company (Ltd)"), # noqa + ("sc", "Cooperative"), + ("asso", "Association"), + ("fond", "Foundation"), + ("edp", "Company under public law"), + ] + ) + + def get_required_field(self): + req_fields = super(SubscriptionRequest, self).get_required_field() + if "iban" in req_fields: + req_fields.remove("iban") + + return req_fields diff --git a/easy_my_coop_ch/models/partner.py b/easy_my_coop_ch/models/partner.py index ec26713..6d67a5a 100644 --- a/easy_my_coop_ch/models/partner.py +++ b/easy_my_coop_ch/models/partner.py @@ -1,16 +1,18 @@ -from odoo import fields, models - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - legal_form = fields.Selection(selection_add=[ - ('ei', 'Individual company'), - ('snc', 'Partnership'), - ('sa', 'Limited company (SA)'), - ('sarl', 'Limited liability company (Ltd)'), - ('sc', 'Cooperative'), - ('asso', 'Association'), - ('fond', 'Foundation'), - ('edp', 'Company under public law') - ]) +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + legal_form = fields.Selection( + selection_add=[ + ("ei", "Individual company"), + ("snc", "Partnership"), + ("sa", "Limited company (SA)"), + ("sarl", "Limited liability company (Ltd)"), + ("sc", "Cooperative"), + ("asso", "Association"), + ("fond", "Foundation"), + ("edp", "Company under public law"), + ] + ) diff --git a/easy_my_coop_ch/views/certificate_template.xml b/easy_my_coop_ch/views/certificate_template.xml deleted file mode 100644 index d2c72da..0000000 --- a/easy_my_coop_ch/views/certificate_template.xml +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/easy_my_coop_dividend/__manifest__.py b/easy_my_coop_dividend/__manifest__.py index 76e72c0..6659e90 100644 --- a/easy_my_coop_dividend/__manifest__.py +++ b/easy_my_coop_dividend/__manifest__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # Copyright (C) 2013-2018 Open Architects Consulting SPRL. # Copyright (C) 2013-2018 Coop IT Easy SCRLfs. # @@ -20,29 +18,16 @@ { - 'name': 'Easy My Coop Dividend Engine', - - 'summary': """ - Manage the dividend calculation for a fiscal year. + "name": "Easy My Coop Dividend Engine", + "summary": """ + Manage the dividend computation for a fiscal year. """, - 'summary': """ - This module allows to calculate the dividend of the cooperative. - """, - - 'author': 'Houssine BAKKALI, ', - 'license': 'AGPL-3', - 'version': '9.0.1.0', - 'website': "www.coopiteasy.be", - - 'category': 'Cooperative Management', - - 'depends': [ - 'easy_my_coop', - ], - - 'data': [ - 'security/ir.model.access.csv', - 'views/dividend_views.xml', - ], - 'installable': False, + "author": "Houssine BAKKALI, ", + "license": "AGPL-3", + "version": "12.0.0.0.1", + "website": "www.coopiteasy.be", + "category": "Cooperative Management", + "depends": ["easy_my_coop"], + "data": ["security/ir.model.access.csv", "views/dividend_views.xml"], + "installable": False, } diff --git a/easy_my_coop_dividend/models/dividend.py b/easy_my_coop_dividend/models/dividend.py index 2682e48..d5a624b 100644 --- a/easy_my_coop_dividend/models/dividend.py +++ b/easy_my_coop_dividend/models/dividend.py @@ -1,190 +1,213 @@ -# -*- coding: utf-8 -*- - -from __future__ import division -from datetime import datetime -import openerp.addons.decimal_precision as dp - -from openerp import fields, models, api - - -class dividend_year(models.Model): - _name = 'dividend.year' - - @api.multi - def _compute_dividend_info(self): - res = {} - for dividend in self: - res[dividend.id] = {'grand_total_dividend': 0.0, - 'grand_total_taxes': 0.0} - for line in dividend.dividend_ids: - res[dividend.id]['grand_total_dividend'] += line.dividend_amount - res[dividend.id]['grand_total_taxes'] += line.dividend_taxes - return res - - name = fields.Char(string='Code') - date_from = fields.Date(string='Date from') - date_to = fields.Date(string='Date to') -# fiscal_year_id = fields.Many2one('account.fiscalyear', -# string='Fiscal year') - percentage = fields.Float(string='Percentage') - withholding_tax = fields.Float(string='Withholding tax') - detailed_dividend_ids = fields.One2many('detailed.dividend.line', - 'dividend_year_id', - string='Dividend lines') - dividend_ids = fields.One2many('dividend.line', - 'dividend_year_id', - string='Dividend lines') - grand_total_dividend = fields.Float( - compute=_compute_dividend_info, - string='Grand total dividend', - digits_compute=dp.get_precision('Account')) - grand_total_taxes = fields.Float( - compute=_compute_dividend_info, - string='Grand total taxes', - digits_compute=dp.get_precision('Account')) - - @api.multi - def compute_dividend(self): - self.ensure_one() - det_div_line_obj = self.env['detailed.dividend.line'] - div_line_obj = self.env['dividend.line'] - res_partner_obj = self.env['res.partner'] - - # delete lines if any - detailed_dividend_ids = det_div_line_obj.search([('dividend_year_id', '=', self.id)]) - detailed_dividend_ids.unlink() - dividend_ids = div_line_obj.search([('dividend_year_id', '=', self.id)]) - dividend_ids.unlink() - - partner_ids = res_partner_obj.search([ - ('cooperator', '=', True), - ('member', '=', True)], - order='cooperator_register_number') - number_of_days = (datetime.strptime(self.date_to, '%Y-%m-%d') - - datetime.strptime(self.date_from, '%Y-%m-%d') - ).days + 1 - - for partner in partner_ids: - total_amount_dividend = 0.0 - for line in partner.share_ids: - vals = {} - vals2 = {} - line_id = False - if line.effective_date >= self.date_from \ - and line.effective_date <= self.date_to: - date_res = (datetime.strptime(self.date_to, '%Y-%m-%d') - - datetime.strptime(line.effective_date, '%Y-%m-%d')).days - coeff = (date_res / number_of_days) * self.percentage - dividend_amount = line.total_amount_line * coeff - - vals['days'] = date_res - vals['dividend_year_id'] = self.id - vals['coop_number'] = line.partner_id.cooperator_register_number - vals['partner_id'] = partner.id - vals['share_line_id'] = line.id - vals['coeff'] = coeff - vals['dividend_amount'] = dividend_amount - total_amount_dividend += dividend_amount - - line_id = det_div_line_obj.create(vals) - elif line.effective_date < self.date_from: - dividend_amount = line.total_amount_line * self.percentage - vals['days'] = number_of_days - vals['dividend_year_id'] = self.id - vals['coop_number'] = line.partner_id.cooperator_register_number - vals['partner_id'] = partner.id - vals['share_line_id'] = line.id - vals['coeff'] = self.percentage - vals['dividend_amount'] = dividend_amount - total_amount_dividend += dividend_amount - - line_id = det_div_line_obj.create(vals) - if line_id: - vals2['coop_number'] = line.partner_id.cooperator_register_number - vals2['dividend_year_id'] = self.id - vals2['partner_id'] = line.partner_id.id - - vals2['dividend_amount_net'] = total_amount_dividend - vals2['dividend_amount'] = total_amount_dividend - - # TODO set as a parameter on dividend year object - if total_amount_dividend <= 190.00: - vals2['dividend_taxes'] = 0.0 - else: - div_tax = (total_amount_dividend - 190) * self.withholding_tax - vals2['dividend_taxes'] = div_tax - vals2['dividend_amount_net'] = total_amount_dividend - div_tax - - div_line_obj.create(vals2) - return True - - -class DetailedDividendLine(models.Model): - _name = 'detailed.dividend.line' - - @api.multi - def _compute_total_line(self): - res = {} - for line in self: - res[line.id] = line.share_unit_price * line.share_number - return res - - dividend_year_id = fields.Many2one('dividend.year', - string='Dividend year') - coop_number = fields.Integer(string='Cooperator Number') - days = fields.Integer(string='Days') - partner_id = fields.Many2one('res.partner', - string='Cooperator', - readonly=True) - share_line_id = fields.Many2one('share.line', - string='Share line', - readonly=True) - share_number = fields.Integer(related='share_line_id.share_number', - string='Number of Share') - share_unit_price = fields.Monetary( - string='Share unit price', - related='share_line_id.share_unit_price', - ) - effective_date = fields.Date(related='share_line_id.effective_date', - string='Effective date') - total_amount_line = fields.Monetary( - string="Total value of share", - currency_field="company_currency_id", - compute=_compute_total_line, - readonly=True, - ) - coeff = fields.Float(string='Coefficient to apply', - digits=(2, 4)) - dividend_amount = fields.Float(string='Gross Dividend') - dividend_amount_net = fields.Float(string='Dividend net') - dividend_taxes = fields.Float(string='Taxes') - company_currency_id = fields.Many2one( - related="share_line_id.company_currency_id", - readonly=True, - ) - - -class dividend_line(models.Model): - _name = 'dividend.line' - - @api.multi - def _get_account_number(self): - res = {} - for line in self: - bank_accounts = self.env['res.partner.bank'].search( - [('partner_id', '=', line.partner_id.id)]) - res[line.id] = bank_accounts[0].acc_number - - return res - - coop_number = fields.Integer(string='Coop Number') - dividend_year_id = fields.Many2one('dividend.year', - string='Dividend year') - partner_id = fields.Many2one('res.partner', - string='Cooperator', - readonly=True) - account_number = fields.Char(compute=_get_account_number, - string='Account Number') - dividend_amount = fields.Float(string='Gross Dividend') - dividend_amount_net = fields.Float(string='Dividend net') - dividend_taxes = fields.Float(string='Taxes') +from __future__ import division + +from datetime import datetime + +import openerp.addons.decimal_precision as dp +from openerp import api, fields, models + + +class DividendYear(models.Model): + _name = "dividend.year" + + @api.multi + def _compute_dividend_info(self): + res = {} + for dividend in self: + res[dividend.id] = { + "grand_total_dividend": 0.0, + "grand_total_taxes": 0.0, + } + for line in dividend.dividend_ids: + res[dividend.id][ + "grand_total_dividend" + ] += line.dividend_amount + res[dividend.id]["grand_total_taxes"] += line.dividend_taxes + return res + + name = fields.Char(string="Code") + date_from = fields.Date(string="Date from") + date_to = fields.Date(string="Date to") + # fiscal_year_id = fields.Many2one('account.fiscalyear', + # string='Fiscal year') + percentage = fields.Float(string="Percentage") + withholding_tax = fields.Float(string="Withholding tax") + detailed_dividend_ids = fields.One2many( + "detailed.dividend.line", "dividend_year_id", string="Dividend lines" + ) + dividend_ids = fields.One2many( + "dividend.line", "dividend_year_id", string="Dividend lines" + ) + grand_total_dividend = fields.Float( + compute=_compute_dividend_info, + string="Grand total dividend", + digits_compute=dp.get_precision("Account"), + ) + grand_total_taxes = fields.Float( + compute=_compute_dividend_info, + string="Grand total taxes", + digits_compute=dp.get_precision("Account"), + ) + + @api.multi + def compute_dividend(self): + self.ensure_one() + det_div_line_obj = self.env["detailed.dividend.line"] + div_line_obj = self.env["dividend.line"] + res_partner_obj = self.env["res.partner"] + + # delete lines if any + detailed_dividend_ids = det_div_line_obj.search( + [("dividend_year_id", "=", self.id)] + ) + detailed_dividend_ids.unlink() + dividend_ids = div_line_obj.search( + [("dividend_year_id", "=", self.id)] + ) + dividend_ids.unlink() + + partner_ids = res_partner_obj.search( + [("cooperator", "=", True), ("member", "=", True)], + order="cooperator_register_number", + ) + number_of_days = ( + datetime.strptime(self.date_to, "%Y-%m-%d") + - datetime.strptime(self.date_from, "%Y-%m-%d") + ).days + 1 + + for partner in partner_ids: + total_amount_dividend = 0.0 + for line in partner.share_ids: + vals = {} + vals2 = {} + line_id = False + if ( + line.effective_date >= self.date_from + and line.effective_date <= self.date_to + ): + date_res = ( + datetime.strptime(self.date_to, "%Y-%m-%d") + - datetime.strptime(line.effective_date, "%Y-%m-%d") + ).days + coeff = (date_res / number_of_days) * self.percentage + dividend_amount = line.total_amount_line * coeff + + vals["days"] = date_res + vals["dividend_year_id"] = self.id + vals[ + "coop_number" + ] = line.partner_id.cooperator_register_number + vals["partner_id"] = partner.id + vals["share_line_id"] = line.id + vals["coeff"] = coeff + vals["dividend_amount"] = dividend_amount + total_amount_dividend += dividend_amount + + line_id = det_div_line_obj.create(vals) + elif line.effective_date < self.date_from: + dividend_amount = line.total_amount_line * self.percentage + vals["days"] = number_of_days + vals["dividend_year_id"] = self.id + vals[ + "coop_number" + ] = line.partner_id.cooperator_register_number + vals["partner_id"] = partner.id + vals["share_line_id"] = line.id + vals["coeff"] = self.percentage + vals["dividend_amount"] = dividend_amount + total_amount_dividend += dividend_amount + + line_id = det_div_line_obj.create(vals) + if line_id: + vals2[ + "coop_number" + ] = line.partner_id.cooperator_register_number + vals2["dividend_year_id"] = self.id + vals2["partner_id"] = line.partner_id.id + + vals2["dividend_amount_net"] = total_amount_dividend + vals2["dividend_amount"] = total_amount_dividend + + # TODO set as a parameter on dividend year object + if total_amount_dividend <= 190.00: + vals2["dividend_taxes"] = 0.0 + else: + div_tax = ( + total_amount_dividend - 190 + ) * self.withholding_tax + vals2["dividend_taxes"] = div_tax + vals2["dividend_amount_net"] = ( + total_amount_dividend - div_tax + ) + + div_line_obj.create(vals2) + return True + + +class DetailedDividendLine(models.Model): + _name = "detailed.dividend.line" + + @api.multi + def _compute_total_line(self): + res = {} + for line in self: + res[line.id] = line.share_unit_price * line.share_number + return res + + dividend_year_id = fields.Many2one("dividend.year", string="Dividend year") + coop_number = fields.Integer(string="Cooperator Number") + days = fields.Integer(string="Days") + partner_id = fields.Many2one( + "res.partner", string="Cooperator", readonly=True + ) + share_line_id = fields.Many2one( + "share.line", string="Share line", readonly=True + ) + share_number = fields.Integer( + related="share_line_id.share_number", string="Number of Share" + ) + share_unit_price = fields.Monetary( + string="Share unit price", related="share_line_id.share_unit_price" + ) + effective_date = fields.Date( + related="share_line_id.effective_date", string="Effective date" + ) + total_amount_line = fields.Monetary( + string="Total value of share", + currency_field="company_currency_id", + compute=_compute_total_line, + readonly=True, + ) + coeff = fields.Float(string="Coefficient to apply", digits=(2, 4)) + dividend_amount = fields.Float(string="Gross Dividend") + dividend_amount_net = fields.Float(string="Dividend net") + dividend_taxes = fields.Float(string="Taxes") + company_currency_id = fields.Many2one( + related="share_line_id.company_currency_id", readonly=True + ) + + +class DividendLine(models.Model): + _name = "dividend.line" + + @api.multi + def _get_account_number(self): + res = {} + for line in self: + bank_accounts = self.env["res.partner.bank"].search( + [("partner_id", "=", line.partner_id.id)] + ) + res[line.id] = bank_accounts[0].acc_number + + return res + + coop_number = fields.Integer(string="Coop Number") + dividend_year_id = fields.Many2one("dividend.year", string="Dividend year") + partner_id = fields.Many2one( + "res.partner", string="Cooperator", readonly=True + ) + account_number = fields.Char( + compute=_get_account_number, string="Account Number" + ) + dividend_amount = fields.Float(string="Gross Dividend") + dividend_amount_net = fields.Float(string="Dividend net") + dividend_taxes = fields.Float(string="Taxes") diff --git a/easy_my_coop_export_xlsx/__manifest__.py b/easy_my_coop_export_xlsx/__manifest__.py index 119703f..e0845f5 100644 --- a/easy_my_coop_export_xlsx/__manifest__.py +++ b/easy_my_coop_export_xlsx/__manifest__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - # Copyright (C) 2018- Coop IT Easy SCRLfs. # # This program is free software: you can redistribute it and/or modify @@ -19,27 +17,18 @@ { - 'name': 'Easy My Coop Export XLSX', - - 'summary': """ + "name": "Easy My Coop Export XLSX", + "summary": """ Generate a xlsx file with information on current state of subscription request, cooperators and capital release request. """, "author": "Coop IT Easy SCRLfs", - 'license': 'AGPL-3', - 'version': '9.0.1.0', - 'website': "www.coopiteasy.be", - - 'category': 'Cooperative Management', - - 'depends': [ - 'easy_my_coop', - ], - 'external_dependencies': { - 'python': ['xlsxwriter'], - }, - 'data': [ - 'wizard/export_global_wizard.xml', - ], - 'installable': False, + "license": "AGPL-3", + "version": "12.0.0.0.1", + "website": "www.coopiteasy.be", + "category": "Cooperative Management", + "depends": ["easy_my_coop"], + "external_dependencies": {"python": ["xlsxwriter"]}, + "data": ["wizard/export_global_wizard.xml"], + "installable": False, } diff --git a/easy_my_coop_export_xlsx/wizard/export_global_wizard.py b/easy_my_coop_export_xlsx/wizard/export_global_wizard.py index ed0d95f..22b490e 100644 --- a/easy_my_coop_export_xlsx/wizard/export_global_wizard.py +++ b/easy_my_coop_export_xlsx/wizard/export_global_wizard.py @@ -1,281 +1,322 @@ -# -*- encoding: utf-8 -*- - -from openerp import fields, models, api - -import time -from cStringIO import StringIO -import base64 - -import xlsxwriter - -HEADER = [ - 'Num. Coop', - 'Nom', - 'Email', - 'Banque', - 'Mobile', - 'Adresse', - 'Rue', - 'Code Postal', - 'Ville', - 'Pays', - 'Nombre de part total', - 'Montant total des parts', - 'Demande de liberation de capital', - 'Communication', - 'Nombre de part', - 'Montant', - 'Reception du paiement', - 'Date de la souscription' - ] -HEADER2 = [ - 'Date de la souscription', - 'Nom', - 'Type', - 'Nombre de part', - 'Montant', - 'Statut', - 'Email', - 'Mobile', - 'Adresse', - 'Code Postal', - 'Ville', - 'Pays', - ] - - -class export_global_report(models.TransientModel): - _name = 'export.global.report' - - name = fields.Char('Name') - - def write_header(self, worksheet, headers): - i = 0 - for header in headers: - worksheet.write(0, i, header) - i += 1 - return True - - @api.multi - def export_global_report_xlsx(self): - partner_obj = self.env['res.partner'] - invoice_obj = self.env['account.invoice'] - subscription_obj = self.env['subscription.request'] - - file_data = StringIO() - workbook = xlsxwriter.Workbook(file_data) - worksheet1 = workbook.add_worksheet() - - self.write_header(worksheet1, HEADER) - cooperators = partner_obj.search([('cooperator', '=', True), - ('member', '=', True)]) - - j = 1 - for coop in cooperators: - i = 0 - worksheet1.write(j, i, coop.cooperator_register_number) - i += 1 - worksheet1.write(j, i, coop.name) - i += 1 - worksheet1.write(j, i, coop.email) - i += 1 - acc_number = "" - if coop.bank_ids: - acc_number = coop.bank_ids[0].acc_number - worksheet1.write(j, i, acc_number) - i += 1 - worksheet1.write(j, i, coop.phone) - i += 1 - address = coop.street + ' ' + coop.zip + ' ' \ - + coop.city + ' ' + coop.country_id.name - worksheet1.write(j, i, address) - i += 1 - worksheet1.write(j, i, coop.street) - i += 1 - worksheet1.write(j, i, int(coop.zip)) - i += 1 - worksheet1.write(j, i, coop.city) - i += 1 - worksheet1.write(j, i, coop.country_id.name) - i += 1 - worksheet1.write(j, i, coop.number_of_share) - i += 1 - worksheet1.write(j, i, coop.total_value) - - invoice_ids = invoice_obj.search([('release_capital_request', '=', True), - ('partner_id', '=', coop.id)]) - j += 1 - for invoice in invoice_ids: - i = 11 - worksheet1.write(j, i, invoice.number) - i += 1 - worksheet1.write(j, i, invoice.state) - i += 1 - worksheet1.write(j, i, invoice.date_invoice) - i += 1 - worksheet1.write(j, i, invoice.reference) - i += 1 - for line in invoice.invoice_line_ids: - worksheet1.write(j, i, line.quantity) - i += 1 - worksheet1.write(j, i, line.price_subtotal) - i += 1 - if invoice.payment_ids: - worksheet1.write(j, i, invoice.payment_ids[0].payment_date) - i += 1 - if invoice.subscription_request: - ind = len(invoice.subscription_request)-1 - worksheet1.write(j, i, invoice.subscription_request[ind].date) - j += 1 - - sub_requests = subscription_obj.search([('state', 'in', - ['draft', 'waiting']), - ('partner_id', '=', coop.id) - ]) - for sub_request in sub_requests: - i = 11 - worksheet1.write(j, i, dict(subscription_obj._columns['type'].selection).get(sub_request.type,False)) - i += 1 - worksheet1.write(j, i, sub_request.state) - i += 3 - quantity = int(sub_request.ordered_parts) - worksheet1.write(j, i, quantity) - i += 1 - amount = quantity * sub_request.share_unit_price - worksheet1.write(j, i, amount) - i += 2 - worksheet1.write(j, i, sub_request.date) - j += 1 - - worksheet1bis = workbook.add_worksheet() - self.write_header(worksheet1bis, HEADER) - cooperators = partner_obj.search([('cooperator', '=', True), - ('member', '=', False)]) - - j = 1 - for coop in cooperators: - i = 0 - worksheet1bis.write(j, i, coop.cooperator_register_number) - i += 1 - worksheet1bis.write(j, i, coop.name) - i += 1 - worksheet1bis.write(j, i, coop.email) - i += 1 - worksheet1bis.write(j, i, coop.phone) - i += 1 - worksheet1bis.write(j, i, coop.street) - i += 1 - worksheet1bis.write(j, i, int(coop.zip)) - i += 1 - worksheet1bis.write(j, i, coop.city) - i += 1 - worksheet1bis.write(j, i, coop.country_id.name) - i += 1 - worksheet1bis.write(j, i, coop.number_of_share) - i += 1 - worksheet1bis.write(j, i, coop.total_value) - - invoice_ids = invoice_obj.search([('release_capital_request', '=', True), - ('partner_id', '=', coop.id)]) - j += 1 - for invoice in invoice_ids: - i = 11 - worksheet1bis.write(j, i, invoice.number) - i += 1 - worksheet1bis.write(j, i, invoice.state) - i += 1 - worksheet1bis.write(j, i, invoice.date_invoice) - i += 1 - worksheet1bis.write(j, i, invoice.reference) - i += 1 - for line in invoice.invoice_line_ids: - worksheet1bis.write(j, i, line.quantity) - i += 1 - worksheet1bis.write(j, i, line.price_subtotal) - i += 1 - if invoice.payment_ids: - worksheet1bis.write(j, i, invoice.payment_ids[0].date) - i += 1 - if invoice.subscription_request: - ind = len(invoice.subscription_request)-1 - worksheet1bis.write(j, i, invoice.subscription_request[ind].date) - j += 1 - - sub_requests = subscription_obj.search([('state', 'in', - ['draft', 'waiting']), - ('partner_id', '=', coop.id) - ]) - for sub_request in sub_requests: - i = 11 - worksheet1bis.write(j, i, dict(subscription_obj._columns['type'].selection).get(sub_request.type,False)) - i += 1 - worksheet1bis.write(j, i, sub_request.state) - i += 3 - quantity = int(sub_request.ordered_parts) - worksheet1bis.write(j, i, quantity) - i += 1 - amount = quantity * sub_request.share_unit_price - worksheet1bis.write(j, i, amount) - i += 2 - worksheet1bis.write(j, i, sub_request.date) - j += 1 - - worksheet2 = workbook.add_worksheet() - self.write_header(worksheet2, HEADER2) - sub_requests = subscription_obj.search([('state', 'in', - ['draft', 'waiting']), - ]) - - j = 1 - for sub_request in sub_requests: - i = 0 - worksheet2.write(j, i, sub_request.date) - i += 1 - worksheet2.write(j, i, sub_request.name) - i += 1 - sub_type_sel = subscription_obj._columns['type'].selection - worksheet2.write(j, i, dict(sub_type_sel).get(sub_request.type, False)) - i += 1 - quantity = int(sub_request.ordered_parts) - worksheet2.write(j, i, quantity) - i += 1 - amount = quantity * sub_request.share_unit_price - worksheet2.write(j, i, amount) - i += 1 - worksheet2.write(j, i, sub_request.state) - i += 1 - worksheet2.write(j, i, sub_request.email) - i += 1 - worksheet2.write(j, i, sub_request.phone) - i += 1 - worksheet2.write(j, i, sub_request.address) - i += 1 - worksheet2.write(j, i, sub_request.city) - i += 1 - worksheet2.write(j, i, int(sub_request.zip_code)) - i += 1 - worksheet2.write(j, i, sub_request.country_id.name) - j += 1 - - workbook.close() - file_data.seek(0) - - data = base64.encodestring(file_data.read()) - - attachment_id = self.env['ir.attachment'].create({ - 'name': "Global export" + time.strftime('%Y-%m-%d %H:%M') - + ".xlsx", - 'datas': data, - 'datas_fname': 'Global_export.xlsx', - 'res_model': 'export.global.report', - },) - - # Prepare your download URL - download_url = '/web/content/' + str(attachment_id.id) + '?download=True' - base_url = self.env['ir.config_parameter'].get_param('web.base.url') - - return { - "type": "ir.actions.act_url", - "url": str(base_url) + str(download_url), - "target": "new", - } +import base64 +import time + +import xlsxwriter +from cStringIO import StringIO +from openerp import api, fields, models + +HEADER = [ + "Num. Coop", + "Nom", + "Email", + "Banque", + "Mobile", + "Adresse", + "Rue", + "Code Postal", + "Ville", + "Pays", + "Nombre de part total", + "Montant total des parts", + "Demande de liberation de capital", + "Communication", + "Nombre de part", + "Montant", + "Reception du paiement", + "Date de la souscription", +] +HEADER2 = [ + "Date de la souscription", + "Nom", + "Type", + "Nombre de part", + "Montant", + "Statut", + "Email", + "Mobile", + "Adresse", + "Code Postal", + "Ville", + "Pays", +] + + +class ExportGlobalReport(models.TransientModel): + _name = "export.global.report" + + name = fields.Char("Name") + + def write_header(self, worksheet, headers): + i = 0 + for header in headers: + worksheet.write(0, i, header) + i += 1 + return True + + @api.multi + def export_global_report_xlsx(self): + partner_obj = self.env["res.partner"] + invoice_obj = self.env["account.invoice"] + subscription_obj = self.env["subscription.request"] + + file_data = StringIO() + workbook = xlsxwriter.Workbook(file_data) + worksheet1 = workbook.add_worksheet() + + self.write_header(worksheet1, HEADER) + cooperators = partner_obj.search( + [("cooperator", "=", True), ("member", "=", True)] + ) + + j = 1 + for coop in cooperators: + i = 0 + worksheet1.write(j, i, coop.cooperator_register_number) + i += 1 + worksheet1.write(j, i, coop.name) + i += 1 + worksheet1.write(j, i, coop.email) + i += 1 + acc_number = "" + if coop.bank_ids: + acc_number = coop.bank_ids[0].acc_number + worksheet1.write(j, i, acc_number) + i += 1 + worksheet1.write(j, i, coop.phone) + i += 1 + address = ( + coop.street + + " " + + coop.zip + + " " + + coop.city + + " " + + coop.country_id.name + ) + worksheet1.write(j, i, address) + i += 1 + worksheet1.write(j, i, coop.street) + i += 1 + worksheet1.write(j, i, int(coop.zip)) + i += 1 + worksheet1.write(j, i, coop.city) + i += 1 + worksheet1.write(j, i, coop.country_id.name) + i += 1 + worksheet1.write(j, i, coop.number_of_share) + i += 1 + worksheet1.write(j, i, coop.total_value) + + invoice_ids = invoice_obj.search( + [ + ("release_capital_request", "=", True), + ("partner_id", "=", coop.id), + ] + ) + j += 1 + for invoice in invoice_ids: + i = 11 + worksheet1.write(j, i, invoice.number) + i += 1 + worksheet1.write(j, i, invoice.state) + i += 1 + worksheet1.write(j, i, invoice.date_invoice) + i += 1 + worksheet1.write(j, i, invoice.reference) + i += 1 + for line in invoice.invoice_line_ids: + worksheet1.write(j, i, line.quantity) + i += 1 + worksheet1.write(j, i, line.price_subtotal) + i += 1 + if invoice.payment_ids: + worksheet1.write(j, i, invoice.payment_ids[0].payment_date) + i += 1 + if invoice.subscription_request: + ind = len(invoice.subscription_request) - 1 + worksheet1.write( + j, i, invoice.subscription_request[ind].date + ) + j += 1 + + sub_requests = subscription_obj.search( + [ + ("state", "in", ["draft", "waiting"]), + ("partner_id", "=", coop.id), + ] + ) + for sub_request in sub_requests: + i = 11 + worksheet1.write( + j, + i, + dict(subscription_obj._columns["type"].selection).get( + sub_request.type, False + ), + ) + i += 1 + worksheet1.write(j, i, sub_request.state) + i += 3 + quantity = int(sub_request.ordered_parts) + worksheet1.write(j, i, quantity) + i += 1 + amount = quantity * sub_request.share_unit_price + worksheet1.write(j, i, amount) + i += 2 + worksheet1.write(j, i, sub_request.date) + j += 1 + + worksheet1bis = workbook.add_worksheet() + self.write_header(worksheet1bis, HEADER) + cooperators = partner_obj.search( + [("cooperator", "=", True), ("member", "=", False)] + ) + + j = 1 + for coop in cooperators: + i = 0 + worksheet1bis.write(j, i, coop.cooperator_register_number) + i += 1 + worksheet1bis.write(j, i, coop.name) + i += 1 + worksheet1bis.write(j, i, coop.email) + i += 1 + worksheet1bis.write(j, i, coop.phone) + i += 1 + worksheet1bis.write(j, i, coop.street) + i += 1 + worksheet1bis.write(j, i, int(coop.zip)) + i += 1 + worksheet1bis.write(j, i, coop.city) + i += 1 + worksheet1bis.write(j, i, coop.country_id.name) + i += 1 + worksheet1bis.write(j, i, coop.number_of_share) + i += 1 + worksheet1bis.write(j, i, coop.total_value) + + invoice_ids = invoice_obj.search( + [ + ("release_capital_request", "=", True), + ("partner_id", "=", coop.id), + ] + ) + j += 1 + for invoice in invoice_ids: + i = 11 + worksheet1bis.write(j, i, invoice.number) + i += 1 + worksheet1bis.write(j, i, invoice.state) + i += 1 + worksheet1bis.write(j, i, invoice.date_invoice) + i += 1 + worksheet1bis.write(j, i, invoice.reference) + i += 1 + for line in invoice.invoice_line_ids: + worksheet1bis.write(j, i, line.quantity) + i += 1 + worksheet1bis.write(j, i, line.price_subtotal) + i += 1 + if invoice.payment_ids: + worksheet1bis.write(j, i, invoice.payment_ids[0].date) + i += 1 + if invoice.subscription_request: + ind = len(invoice.subscription_request) - 1 + worksheet1bis.write( + j, i, invoice.subscription_request[ind].date + ) + j += 1 + + sub_requests = subscription_obj.search( + [ + ("state", "in", ["draft", "waiting"]), + ("partner_id", "=", coop.id), + ] + ) + for sub_request in sub_requests: + i = 11 + worksheet1bis.write( + j, + i, + dict(subscription_obj._columns["type"].selection).get( + sub_request.type, False + ), + ) + i += 1 + worksheet1bis.write(j, i, sub_request.state) + i += 3 + quantity = int(sub_request.ordered_parts) + worksheet1bis.write(j, i, quantity) + i += 1 + amount = quantity * sub_request.share_unit_price + worksheet1bis.write(j, i, amount) + i += 2 + worksheet1bis.write(j, i, sub_request.date) + j += 1 + + worksheet2 = workbook.add_worksheet() + self.write_header(worksheet2, HEADER2) + sub_requests = subscription_obj.search( + [("state", "in", ["draft", "waiting"])] + ) + + j = 1 + for sub_request in sub_requests: + i = 0 + worksheet2.write(j, i, sub_request.date) + i += 1 + worksheet2.write(j, i, sub_request.name) + i += 1 + sub_type_sel = subscription_obj._columns["type"].selection + worksheet2.write( + j, i, dict(sub_type_sel).get(sub_request.type, False) + ) + i += 1 + quantity = int(sub_request.ordered_parts) + worksheet2.write(j, i, quantity) + i += 1 + amount = quantity * sub_request.share_unit_price + worksheet2.write(j, i, amount) + i += 1 + worksheet2.write(j, i, sub_request.state) + i += 1 + worksheet2.write(j, i, sub_request.email) + i += 1 + worksheet2.write(j, i, sub_request.phone) + i += 1 + worksheet2.write(j, i, sub_request.address) + i += 1 + worksheet2.write(j, i, sub_request.city) + i += 1 + worksheet2.write(j, i, int(sub_request.zip_code)) + i += 1 + worksheet2.write(j, i, sub_request.country_id.name) + j += 1 + + workbook.close() + file_data.seek(0) + + data = base64.encodestring(file_data.read()) + + attachment_id = self.env["ir.attachment"].create( + { + "name": "Global export" + + time.strftime("%Y-%m-%d %H:%M") + + ".xlsx", + "datas": data, + "datas_fname": "Global_export.xlsx", + "res_model": "export.global.report", + } + ) + + # Prepare your download URL + download_url = ( + "/web/content/" + str(attachment_id.id) + "?download=True" + ) + base_url = self.env["ir.config_parameter"].get_param("web.base.url") + + return { + "type": "ir.actions.act_url", + "url": str(base_url) + str(download_url), + "target": "new", + } diff --git a/easy_my_coop_fr/__manifest__.py b/easy_my_coop_fr/__manifest__.py index de6ca6a..b8d2b0f 100644 --- a/easy_my_coop_fr/__manifest__.py +++ b/easy_my_coop_fr/__manifest__.py @@ -5,18 +5,17 @@ { "name": "Easy My Coop Fr", "version": "12.0.1.0.1", - "depends": ["easy_my_coop", - "l10n_fr"], + "depends": ["easy_my_coop", "l10n_fr"], "author": "Houssine BAKKALI ", "category": "Cooperative management", - 'webstite': "www.coopiteasy.be", + "webstite": "www.coopiteasy.be", "license": "AGPL-3", - "description": """ - This is the french localization for the easy my coop module + "summary": """ + This is the french localization for the easy my coop module """, - 'data': [ - 'views/subscription_template.xml', - 'views/certificate_template.xml' + "data": [ + "views/subscription_template.xml", + "views/certificate_template.xml", ], - 'installable': True, + "installable": True, } diff --git a/easy_my_coop_fr/models/coop.py b/easy_my_coop_fr/models/coop.py index 99501fa..d5558ac 100644 --- a/easy_my_coop_fr/models/coop.py +++ b/easy_my_coop_fr/models/coop.py @@ -1,19 +1,21 @@ -from odoo import fields, models - - -class SubscriptionRequest(models.Model): - _inherit = 'subscription.request' - - company_type = fields.Selection(selection_add=[ - ('asso', 'Association'), - ('eurl', 'EURL / Entreprise individuelle'), - ('sarl', 'SARL'), - ('sa', 'SA / SAS') - ]) - - def get_required_field(self): - req_fields = super(SubscriptionRequest, self).get_required_field() - if 'iban' in req_fields: - req_fields.remove('iban') - - return req_fields +from odoo import fields, models + + +class SubscriptionRequest(models.Model): + _inherit = "subscription.request" + + company_type = fields.Selection( + selection_add=[ + ("asso", "Association"), + ("eurl", "EURL / Entreprise individuelle"), + ("sarl", "SARL"), + ("sa", "SA / SAS"), + ] + ) + + def get_required_field(self): + req_fields = super(SubscriptionRequest, self).get_required_field() + if "iban" in req_fields: + req_fields.remove("iban") + + return req_fields diff --git a/easy_my_coop_fr/models/partner.py b/easy_my_coop_fr/models/partner.py index 212ce38..682fe93 100644 --- a/easy_my_coop_fr/models/partner.py +++ b/easy_my_coop_fr/models/partner.py @@ -1,12 +1,14 @@ -from odoo import fields, models - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - legal_form = fields.Selection(selection_add=[ - ('asso', 'Association'), - ('eurl', 'EURL / Entreprise individuelle'), - ('sarl', 'SARL'), - ('sa', 'SA / SAS') - ]) +from odoo import fields, models + + +class ResPartner(models.Model): + _inherit = "res.partner" + + legal_form = fields.Selection( + selection_add=[ + ("asso", "Association"), + ("eurl", "EURL / Entreprise individuelle"), + ("sarl", "SARL"), + ("sa", "SA / SAS"), + ] + ) diff --git a/easy_my_coop_loan/__manifest__.py b/easy_my_coop_loan/__manifest__.py index 7a39db5..f59bd67 100644 --- a/easy_my_coop_loan/__manifest__.py +++ b/easy_my_coop_loan/__manifest__.py @@ -1,30 +1,26 @@ -# Copyright 2019 Coop IT Easy SCRLfs () -# - Houssine BAKKALI - -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -{ - "name": "Easy My Coop Bond and Subordinated Loan Issues", - "version": "12.0.1.0.1", - "depends": [ - "easy_my_coop", - ], - "author": "Coop IT Easy SCRLfs", - "category": "Cooperative management", - "website": "http://www.coopiteasy.be", - "license": "AGPL-3", - "description": """ - This module allows to manage the bonds and subordinated loans subscription - life cycle. - """, - 'data': [ - 'security/ir.model.access.csv', - 'views/loan_view.xml', - 'views/partner_view.xml', - 'views/menus.xml', - 'data/mail_template_data.xml', - ], - "demo": [ - "demo/coop.xml", - ], - 'installable': True, -} +# Copyright 2019 Coop IT Easy SCRLfs () +# - Houssine BAKKALI - +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Easy My Coop Bond and Subordinated Loan Issues", + "version": "12.0.1.0.1", + "depends": ["easy_my_coop"], + "author": "Coop IT Easy SCRLfs", + "category": "Cooperative management", + "website": "http://www.coopiteasy.be", + "license": "AGPL-3", + "summary": """ + This module allows to manage the bonds and + subordinated loans subscription life cycle. + """, + "data": [ + "security/ir.model.access.csv", + "views/loan_view.xml", + "views/partner_view.xml", + "views/menus.xml", + "data/mail_template_data.xml", + ], + "demo": ["demo/coop.xml"], + "installable": True, +} diff --git a/easy_my_coop_loan/models/interest_line.py b/easy_my_coop_loan/models/interest_line.py index 23d0670..98a7927 100644 --- a/easy_my_coop_loan/models/interest_line.py +++ b/easy_my_coop_loan/models/interest_line.py @@ -6,59 +6,79 @@ from odoo import fields, models class LoanInterestLine(models.Model): - _name = 'loan.interest.line' + _name = "loan.interest.line" _description = "Loan Interest Line" - name = fields.Integer(string="Year", - required=True) - issue_line = fields.Many2one('loan.issue.line', - string="Subscribed loan", - required=True) - partner_id = fields.Many2one(related='issue_line.partner_id', - store=True, - readlonly=True) - amount = fields.Monetary(related='issue_line.amount', - string="Subscribed amount", - currency_field='company_currency_id', - readonly=True) - interest = fields.Monetary(string="Gross interest amount", - currency_field='company_currency_id', - readonly=True) - net_interest = fields.Monetary(string="Net interest amount", - currency_field='company_currency_id', - readonly=True) - taxes_rate = fields.Float(string="Taxes on interest", - required=True) - taxes_amount = fields.Monetary(string="Taxes amount", - currency_field='company_currency_id', - readonly=True) - accrued_amount = fields.Monetary(string="Accrued amount", - currency_field='company_currency_id', - readonly=True) - accrued_interest = fields.Monetary(string="Accrued gross interest", - currency_field='company_currency_id', - readonly=True) + name = fields.Integer(string="Year", required=True) + issue_line = fields.Many2one( + "loan.issue.line", string="Subscribed loan", required=True + ) + partner_id = fields.Many2one( + related="issue_line.partner_id", store=True, readlonly=True + ) + amount = fields.Monetary( + related="issue_line.amount", + string="Subscribed amount", + currency_field="company_currency_id", + readonly=True, + ) + interest = fields.Monetary( + string="Gross interest amount", + currency_field="company_currency_id", + readonly=True, + ) + net_interest = fields.Monetary( + string="Net interest amount", + currency_field="company_currency_id", + readonly=True, + ) + taxes_rate = fields.Float(string="Taxes on interest", required=True) + taxes_amount = fields.Monetary( + string="Taxes amount", + currency_field="company_currency_id", + readonly=True, + ) + accrued_amount = fields.Monetary( + string="Accrued amount", + currency_field="company_currency_id", + readonly=True, + ) + accrued_interest = fields.Monetary( + string="Accrued gross interest", + currency_field="company_currency_id", + readonly=True, + ) accrued_net_interest = fields.Monetary( - string="Accrued net interest", - currency_field='company_currency_id', - readonly=True) - accrued_taxes = fields.Monetary(string="Accrued taxes to pay", - currency_field='company_currency_id', - readonly=True) + string="Accrued net interest", + currency_field="company_currency_id", + readonly=True, + ) + accrued_taxes = fields.Monetary( + string="Accrued taxes to pay", + currency_field="company_currency_id", + readonly=True, + ) due_date = fields.Date(string="Due date") - company_currency_id = fields.Many2one('res.currency', - related='company_id.currency_id', - string="Company Currency", - readonly=True) - company_id = fields.Many2one('res.company', - related='issue_line.company_id', - string="Company", - readonly=True) - state = fields.Selection([('draft', 'Draft'), - ('due', 'Due'), - ('requested', 'Payment requested'), - ('donation', 'Donation'), - ('paid', 'Paid') - ], - string="State", - default="draft") + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + company_id = fields.Many2one( + "res.company", + related="issue_line.company_id", + string="Company", + readonly=True, + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("due", "Due"), + ("requested", "Payment requested"), + ("donation", "Donation"), + ("paid", "Paid"), + ], + string="State", + default="draft", + ) diff --git a/easy_my_coop_loan/models/loan.py b/easy_my_coop_loan/models/loan.py index c1dac6d..0e686c5 100644 --- a/easy_my_coop_loan/models/loan.py +++ b/easy_my_coop_loan/models/loan.py @@ -4,81 +4,103 @@ import logging -from odoo import api, fields, models, _ +from odoo import _, api, fields, models _logger = logging.getLogger(__name__) class LoanIssue(models.Model): - _name = 'loan.issue' - _description = 'Loan Issue' + _name = "loan.issue" + _description = "Loan Issue" @api.multi def _compute_subscribed_amount(self): for issue in self: susbscribed_amount = 0.0 for line in issue.loan_issue_lines.filtered( - lambda record: record.state != 'cancelled'): + lambda record: record.state != "cancelled" + ): susbscribed_amount += line.amount issue.subscribed_amount = susbscribed_amount - name = fields.Char(string="Name", - translate=True) + name = fields.Char(string="Name", translate=True) default_issue = fields.Boolean(string="Default issue") - subscription_start_date = fields.Date(string="Start date subscription period") + subscription_start_date = fields.Date( + string="Start date subscription period" + ) subscription_end_date = fields.Date(string="End date subscription period") - user_id = fields.Many2one('res.users', - string="Responsible") + user_id = fields.Many2one("res.users", string="Responsible") loan_start_date = fields.Date(string="Loan start date") term_date = fields.Date(string="Term date") loan_term = fields.Float(string="Duration of the loan in month") rate = fields.Float(string="Interest rate") - face_value = fields.Monetary(string="Facial value", - currency_field='company_currency_id', - required=True) - minimum_amount = fields.Monetary(string="Minimum amount of issue", - currency_field='company_currency_id') - maximum_amount = fields.Monetary(string="Maximum amount of issue", - currency_field='company_currency_id') - min_amount_company = fields.Monetary(string="Minimum amount for a company", - currency_field='company_currency_id') - max_amount_company = fields.Monetary(string="Maximum amount for a company", - currency_field='company_currency_id') - min_amount_person = fields.Monetary(string="Minimum amount for a person", - currency_field='company_currency_id') - max_amount_person = fields.Monetary(string="Maximum amount for a person", - currency_field='company_currency_id') - subscribed_amount = fields.Monetary(string="Subscribed amount", - compute="_compute_subscribed_amount", - currency_field='company_currency_id') - interest_payment = fields.Selection([('end', 'End'), - ('yearly', 'Yearly')], - string="Interest payment") + face_value = fields.Monetary( + string="Facial value", + currency_field="company_currency_id", + required=True, + ) + minimum_amount = fields.Monetary( + string="Minimum amount of issue", currency_field="company_currency_id" + ) + maximum_amount = fields.Monetary( + string="Maximum amount of issue", currency_field="company_currency_id" + ) + min_amount_company = fields.Monetary( + string="Minimum amount for a company", + currency_field="company_currency_id", + ) + max_amount_company = fields.Monetary( + string="Maximum amount for a company", + currency_field="company_currency_id", + ) + min_amount_person = fields.Monetary( + string="Minimum amount for a person", + currency_field="company_currency_id", + ) + max_amount_person = fields.Monetary( + string="Maximum amount for a person", + currency_field="company_currency_id", + ) + subscribed_amount = fields.Monetary( + string="Subscribed amount", + compute="_compute_subscribed_amount", + currency_field="company_currency_id", + ) + interest_payment = fields.Selection( + [("end", "End"), ("yearly", "Yearly")], string="Interest payment" + ) interest_payment_info = fields.Char(string="Yearly payment on") - loan_issue_lines = fields.One2many('loan.issue.line', - 'loan_issue_id', - string="Loan issue lines") - state = fields.Selection([('draft', 'Draft'), - ('confirmed', 'Confirmed'), - ('cancelled', 'Cancelled'), - ('ongoing', 'Ongoing'), - ('closed', 'Closed')], - string="State", - default='draft') - company_currency_id = fields.Many2one('res.currency', - related='company_id.currency_id', - string="Company Currency", - readonly=True) - company_id = fields.Many2one('res.company', - string='Company', - required=True, - readonly=True, - default=lambda self: self.env['res.company']._company_default_get()) # noqa + loan_issue_lines = fields.One2many( + "loan.issue.line", "loan_issue_id", string="Loan issue lines" + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("confirmed", "Confirmed"), + ("cancelled", "Cancelled"), + ("ongoing", "Ongoing"), + ("closed", "Closed"), + ], + string="State", + default="draft", + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + company_id = fields.Many2one( + "res.company", + string="Company", + required=True, + readonly=True, + default=lambda self: self.env["res.company"]._company_default_get(), + ) # noqa by_company = fields.Boolean(string="By company") - by_individual = fields.Boolean(string='By individuals') - display_on_website = fields.Boolean(sting='Display on website') - taxes_rate = fields.Float(string="Taxes on interest", - required=True) + by_individual = fields.Boolean(string="By individuals") + display_on_website = fields.Boolean(sting="Display on website") + taxes_rate = fields.Float(string="Taxes on interest", required=True) @api.multi def get_max_amount(self, partner): @@ -88,7 +110,8 @@ class LoanIssue(models.Model): """ self.ensure_one() lines = self.loan_issue_lines.filtered( - lambda r: r.partner_id == partner and r.state != 'cancelled') + lambda r: r.partner_id == partner and r.state != "cancelled" + ) already_subscribed = sum(line.amount for line in lines) max_amount = -1 # No max amount if partner.is_company and self.max_amount_company > 0: @@ -105,7 +128,8 @@ class LoanIssue(models.Model): """ self.ensure_one() lines = self.loan_issue_lines.filtered( - lambda r: r.partner_id == partner and r.state != 'cancelled') + lambda r: r.partner_id == partner and r.state != "cancelled" + ) amount_subscribed = sum(line.amount for line in lines) if partner.is_company: min_amount = self.min_amount_company - amount_subscribed @@ -115,42 +139,41 @@ class LoanIssue(models.Model): @api.multi def get_web_issues(self, is_company): - bond_issues = self.search([ - ('display_on_website', '=', True), - ('state', '=', 'ongoing') - ]) + bond_issues = self.search( + [("display_on_website", "=", True), ("state", "=", "ongoing")] + ) if is_company is True: - return bond_issues.filtered('by_company') + return bond_issues.filtered("by_company") else: - return bond_issues.filtered('by_company') + return bond_issues.filtered("by_company") @api.multi def action_confirm(self): self.ensure_one() - self.write({'state': 'confirmed'}) + self.write({"state": "confirmed"}) @api.multi def action_open(self): self.ensure_one() - self.write({'state': 'ongoing'}) + self.write({"state": "ongoing"}) @api.multi def action_draft(self): self.ensure_one() - self.write({'state': 'draft'}) + self.write({"state": "draft"}) @api.multi def action_cancel(self): self.ensure_one() - self.write({'state': 'cancelled'}) + self.write({"state": "cancelled"}) @api.multi def action_close(self): self.ensure_one() - self.write({'state': 'closed'}) + self.write({"state": "closed"}) def get_interest_vals(self, line, vals): - interest_obj = self.env['loan.interest.line'] + interest_obj = self.env["loan.interest.line"] accrued_amount = line.amount accrued_interest = 0 accrued_net_interest = 0 @@ -163,38 +186,43 @@ class LoanIssue(models.Model): accrued_interest += interest accrued_net_interest += net_interest accrued_taxes += taxes_amount - vals['interest'] = interest - vals['net_interest'] = net_interest - vals['taxes_amount'] = taxes_amount - vals['accrued_amount'] = accrued_amount - vals['accrued_interest'] = accrued_interest - vals['accrued_net_interest'] = accrued_net_interest - vals['accrued_taxes'] = accrued_taxes - vals['name'] = year + vals["interest"] = interest + vals["net_interest"] = net_interest + vals["taxes_amount"] = taxes_amount + vals["accrued_amount"] = accrued_amount + vals["accrued_interest"] = accrued_interest + vals["accrued_net_interest"] = accrued_net_interest + vals["accrued_taxes"] = accrued_taxes + vals["name"] = year interest_obj.create(vals) @api.multi def compute_loan_interest(self): self.ensure_one() - if self.interest_payment == 'end': + if self.interest_payment == "end": due_date = self.term_date else: - raise NotImplementedError(_("Interest payment by year hasn't been " - "implemented yet")) + raise NotImplementedError( + _("Interest payment by year hasn't been " "implemented yet") + ) for line in self.loan_issue_lines: # TODO remove this line line.interest_lines.unlink() # Please Do not Forget vals = { - 'issue_line': line.id, - 'due_date': due_date, - 'taxes_rate': self.taxes_rate - } + "issue_line": line.id, + "due_date": due_date, + "taxes_rate": self.taxes_rate, + } self.get_interest_vals(line, vals) rounded_term = int(self.loan_term) if self.loan_term - rounded_term > 0: # TODO Handle this case - raise NotImplementedError(_("Calculation on non entire year " - "hasn't been implemented yet")) + raise NotImplementedError( + _( + "Calculation on non entire year " + "hasn't been implemented yet" + ) + ) diff --git a/easy_my_coop_loan/models/loan_issue_line.py b/easy_my_coop_loan/models/loan_issue_line.py index e31bfe9..3bb45f9 100644 --- a/easy_my_coop_loan/models/loan_issue_line.py +++ b/easy_my_coop_loan/models/loan_issue_line.py @@ -8,65 +8,79 @@ from odoo import api, fields, models class LoanIssueLine(models.Model): - _name = 'loan.issue.line' - _description = 'Loan Issue Line' - _order = 'date desc, id' + _name = "loan.issue.line" + _description = "Loan Issue Line" + _order = "date desc, id" @api.multi - @api.depends('quantity', 'face_value') + @api.depends("quantity", "face_value") def _compute_amount(self): for line in self: line.amount = line.face_value * line.quantity name = fields.Char(string="Reference") - loan_issue_id = fields.Many2one('loan.issue', - string="Loan issue", - required=True) - interest_lines = fields.One2many('loan.interest.line', - 'issue_line', - string="Interest lines") - quantity = fields.Integer(string='quantity', - required=True) - face_value = fields.Monetary(related='loan_issue_id.face_value', - currency_field='company_currency_id', - store=True, - readonly=True) - partner_id = fields.Many2one('res.partner', - string="Subscriber", - required=True) - date = fields.Date(string="Subscription date", - default=lambda self: datetime.strftime(datetime.now(), - '%Y-%m-%d'), - required=True) - amount = fields.Monetary(string="Subscribed amount", - currency_field='company_currency_id', - compute='_compute_amount', - store=True) - state = fields.Selection([('draft', 'Draft'), - ('subscribed', 'Subscribed'), - ('waiting', 'Waiting payment'), - ('paid', 'paid'), - ('cancelled', 'Cancelled'), - ('ended', 'Ended')], - string="State", - required=True, - default="draft") - company_currency_id = fields.Many2one('res.currency', - related='company_id.currency_id', - string="Company Currency", - readonly=True) - company_id = fields.Many2one('res.company', - related='loan_issue_id.company_id', - string="Company", - readonly=True) + loan_issue_id = fields.Many2one( + "loan.issue", string="Loan issue", required=True + ) + interest_lines = fields.One2many( + "loan.interest.line", "issue_line", string="Interest lines" + ) + quantity = fields.Integer(string="quantity", required=True) + face_value = fields.Monetary( + related="loan_issue_id.face_value", + currency_field="company_currency_id", + store=True, + readonly=True, + ) + partner_id = fields.Many2one( + "res.partner", string="Subscriber", required=True + ) + date = fields.Date( + string="Subscription date", + default=lambda self: datetime.strftime(datetime.now(), "%Y-%m-%d"), + required=True, + ) + amount = fields.Monetary( + string="Subscribed amount", + currency_field="company_currency_id", + compute="_compute_amount", + store=True, + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("subscribed", "Subscribed"), + ("waiting", "Waiting payment"), + ("paid", "paid"), + ("cancelled", "Cancelled"), + ("ended", "Ended"), + ], + string="State", + required=True, + default="draft", + ) + company_currency_id = fields.Many2one( + "res.currency", + related="company_id.currency_id", + string="Company Currency", + readonly=True, + ) + company_id = fields.Many2one( + "res.company", + related="loan_issue_id.company_id", + string="Company", + readonly=True, + ) def get_loan_sub_mail_template(self): - return self.env.ref('easy_my_coop_loan.loan_subscription_confirmation', - False) + return self.env.ref( + "easy_my_coop_loan.loan_subscription_confirmation", False + ) def get_loan_pay_req_mail_template(self): - return self.env.ref('easy_my_coop_loan.loan_issue_payment_request', - False) + return self.env.ref( + "easy_my_coop_loan.loan_issue_payment_request", False + ) @api.model def create(self, vals): @@ -80,12 +94,12 @@ class LoanIssueLine(models.Model): @api.multi def action_draft(self): for line in self: - line.write({'state': 'draft'}) + line.write({"state": "draft"}) @api.multi def action_validate(self): for line in self: - line.write({'state': 'subscribed'}) + line.write({"state": "subscribed"}) @api.multi def action_request_payment(self): @@ -93,14 +107,14 @@ class LoanIssueLine(models.Model): for line in self: pay_req_mail_template.send_mail(line.id) - line.write({'state': 'waiting'}) + line.write({"state": "waiting"}) @api.multi def action_cancel(self): for line in self: - line.write({'state': 'cancelled'}) + line.write({"state": "cancelled"}) @api.multi def action_paid(self): for line in self: - line.write({'state': 'paid'}) + line.write({"state": "paid"}) diff --git a/easy_my_coop_loan/models/partner.py b/easy_my_coop_loan/models/partner.py index 4431673..992bf96 100644 --- a/easy_my_coop_loan/models/partner.py +++ b/easy_my_coop_loan/models/partner.py @@ -3,7 +3,7 @@ # Robin Keunen # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo import fields, models, api +from odoo import api, fields, models class ResPartner(models.Model): @@ -15,9 +15,7 @@ class ResPartner(models.Model): string="Loans", ) is_loaner = fields.Boolean( - string="Loaner", - compute="_compute_is_loaner", - store=True, + string="Loaner", compute="_compute_is_loaner", store=True ) @api.multi diff --git a/easy_my_coop_loan/tests/test_emc_loan.py b/easy_my_coop_loan/tests/test_emc_loan.py index fda5895..bd3f22f 100644 --- a/easy_my_coop_loan/tests/test_emc_loan.py +++ b/easy_my_coop_loan/tests/test_emc_loan.py @@ -2,10 +2,12 @@ # Robin Keunen # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). -from odoo.addons.easy_my_coop.tests.test_base import EMCBaseCase -from odoo.fields import Date from datetime import timedelta + from odoo.exceptions import AccessError +from odoo.fields import Date + +from odoo.addons.easy_my_coop.tests.test_base import EMCBaseCase class EMCLoanCase(EMCBaseCase): @@ -101,4 +103,3 @@ class EMCLoanCase(EMCBaseCase): line.action_paid() loan_issue.compute_loan_interest() - diff --git a/easy_my_coop_loan_website/__manifest__.py b/easy_my_coop_loan_website/__manifest__.py index dce93fa..2962857 100644 --- a/easy_my_coop_loan_website/__manifest__.py +++ b/easy_my_coop_loan_website/__manifest__.py @@ -1,26 +1,19 @@ -# Copyright 2019 Coop IT Easy SCRLfs () -# - Houssine BAKKALI - -# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). - -{ - "name": "Easy My Coop Loan Issues Website", - "version": "12.0.1.0.1", - "depends": [ - "easy_my_coop_loan", - "easy_my_coop_website", - "website", - ], - "author": "Coop IT Easy SCRLfs", - "category": "Cooperative management", - "website": "http://www.coopiteasy.be", - "license": "AGPL-3", - "description": """ - This module implements the subscription page for bonds and - subordinated loans. - """, - 'data': [ - 'data/website_loan_data.xml', - 'template/loan_issue_template.xml' - ], - 'installable': True, -} +# Copyright 2019 Coop IT Easy SCRLfs () +# - Houssine BAKKALI - +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). + +{ + "name": "Easy My Coop Loan Issues Website", + "version": "12.0.1.0.1", + "depends": ["easy_my_coop_loan", "easy_my_coop_website", "website"], + "author": "Coop IT Easy SCRLfs", + "category": "Cooperative management", + "website": "http://www.coopiteasy.be", + "license": "AGPL-3", + "summary": """ + This module implements the subscription page + for bonds and subordinated loans. + """, + "data": ["data/website_loan_data.xml", "template/loan_issue_template.xml"], + "installable": True, +} diff --git a/easy_my_coop_loan_website/controllers/main.py b/easy_my_coop_loan_website/controllers/main.py index 09f7379..e16271e 100644 --- a/easy_my_coop_loan_website/controllers/main.py +++ b/easy_my_coop_loan_website/controllers/main.py @@ -1,95 +1,103 @@ -from odoo import http -from odoo.http import request - -from odoo.tools.translate import _ - - -class WebsiteLoanIssueSubscription(http.Controller): - - @http.route(['/subscription/get_loan_issue'], - type='json', - auth="user", - methods=['POST'], website=True) - def get_loan_issue(self, loan_issue_id, **kw): - loan_issue_obj = request.env['loan.issue'] - partner = request.env.user.partner_id - if loan_issue_id: - loan_issue = loan_issue_obj.sudo().browse(int(loan_issue_id)) - max_amount = loan_issue.get_max_amount(partner) - return { - loan_issue.id: { - 'max_amount': max_amount, - 'face_value': loan_issue.face_value, - } - } - else: - return False - - @http.route(['/subscription/loan_issue_form'], - type='http', auth="user", website=True) - def display_loan_issue_subscription_page(self, **kwargs): - values = {} - partner = request.env.user.partner_id - is_company = partner.is_company - - values = self.fill_values(values, is_company) - values.update(kwargs=kwargs.items()) - return request.render( - "easy_my_coop_loan_website.loanissuesubscription", - values) - - def get_loan_issues(self, is_company): - loan_obj = request.env['loan.issue'] - loan_issues = loan_obj.sudo().get_web_issues(is_company) - - return loan_issues - - def fill_values(self, values, is_company): - company = request.website.company_id - loan_issues = self.get_loan_issues(is_company) - - values['loan_issues'] = loan_issues - values['company'] = company - - if not values.get('loan_issue_id'): - for loan_issue in loan_issues: - if loan_issue.default_issue is True: - values['loan_issue_id'] = loan_issue.id - break - if not values.get('loan_issue_id', False) and loan_issues: - values['loan_issue_id'] = loan_issues[0].id - - return values - - def validation(self, loan_issue, kwargs): - sub_amount = kwargs.get('subscription_amount') - redirect = "easy_my_coop_loan_website.loanissuesubscription" - - values = {} - if not loan_issue: - values["error_msg"] = _("The selected loan issue is not found") - return request.render(redirect, values) - if sub_amount: - values["error_msg"] = _("The amount shoud be of monetary type") - return request.render(redirect, values) - return True - - @http.route(['/subscription/subscribe_loan_issue'], - type='http', - auth="user", website=True) - def loan_issue_subscription(self, **kwargs): - loan_obj = request.env['loan.issue'] - loan_obj_line = request.env['loan.issue.line'] - - loan_issue = loan_obj.sudo().browse(kwargs.get('loan_issue_id')) - partner = request.env.user.partner_id - - if self.validation(loan_issue, kwargs): - values = { - 'loan_issue_id': loan_issue.id, - 'partner_id': partner.id, - 'amount': kwargs['subscription_amount'], - 'state': 'subscribed' - } - loan_obj_line.sudo().create(values) - return request.render("easy_my_coop_website.cooperator_thanks", values) +from odoo import http +from odoo.http import request +from odoo.tools.translate import _ + + +class WebsiteLoanIssueSubscription(http.Controller): + @http.route( + ["/subscription/get_loan_issue"], + type="json", + auth="user", + methods=["POST"], + website=True, + ) + def get_loan_issue(self, loan_issue_id, **kw): + loan_issue_obj = request.env["loan.issue"] + partner = request.env.user.partner_id + if loan_issue_id: + loan_issue = loan_issue_obj.sudo().browse(int(loan_issue_id)) + max_amount = loan_issue.get_max_amount(partner) + return { + loan_issue.id: { + "max_amount": max_amount, + "face_value": loan_issue.face_value, + } + } + else: + return False + + @http.route( + ["/subscription/loan_issue_form"], + type="http", + auth="user", + website=True, + ) + def display_loan_issue_subscription_page(self, **kwargs): + values = {} + partner = request.env.user.partner_id + is_company = partner.is_company + + values = self.fill_values(values, is_company) + values.update(kwargs=kwargs.items()) + return request.render( + "easy_my_coop_loan_website.loanissuesubscription", values + ) + + def get_loan_issues(self, is_company): + loan_obj = request.env["loan.issue"] + loan_issues = loan_obj.sudo().get_web_issues(is_company) + + return loan_issues + + def fill_values(self, values, is_company): + company = request.website.company_id + loan_issues = self.get_loan_issues(is_company) + + values["loan_issues"] = loan_issues + values["company"] = company + + if not values.get("loan_issue_id"): + for loan_issue in loan_issues: + if loan_issue.default_issue is True: + values["loan_issue_id"] = loan_issue.id + break + if not values.get("loan_issue_id", False) and loan_issues: + values["loan_issue_id"] = loan_issues[0].id + + return values + + def validation(self, loan_issue, kwargs): + sub_amount = kwargs.get("subscription_amount") + redirect = "easy_my_coop_loan_website.loanissuesubscription" + + values = {} + if not loan_issue: + values["error_msg"] = _("The selected loan issue is not found") + return request.render(redirect, values) + if sub_amount: + values["error_msg"] = _("The amount shoud be of monetary type") + return request.render(redirect, values) + return True + + @http.route( + ["/subscription/subscribe_loan_issue"], + type="http", + auth="user", + website=True, + ) + def loan_issue_subscription(self, **kwargs): + loan_obj = request.env["loan.issue"] + loan_obj_line = request.env["loan.issue.line"] + + loan_issue = loan_obj.sudo().browse(kwargs.get("loan_issue_id")) + partner = request.env.user.partner_id + + if self.validation(loan_issue, kwargs): + values = { + "loan_issue_id": loan_issue.id, + "partner_id": partner.id, + "amount": kwargs["subscription_amount"], + "state": "subscribed", + } + loan_obj_line.sudo().create(values) + return request.render("easy_my_coop_website.cooperator_thanks", values) diff --git a/easy_my_coop_taxshelter_report/__manifest__.py b/easy_my_coop_taxshelter_report/__manifest__.py index e30b638..c9a6584 100644 --- a/easy_my_coop_taxshelter_report/__manifest__.py +++ b/easy_my_coop_taxshelter_report/__manifest__.py @@ -15,7 +15,7 @@ "category": "Cooperative management", "website": "www.coopiteasy.be", "license": "AGPL-3", - "description": """ + "summary": """ This module allows you to create a fiscal declaration year and to print tax shelter declaration for each cooperator. """, diff --git a/easy_my_coop_taxshelter_report/models/mail_template.py b/easy_my_coop_taxshelter_report/models/mail_template.py index 6495bd1..59811f7 100644 --- a/easy_my_coop_taxshelter_report/models/mail_template.py +++ b/easy_my_coop_taxshelter_report/models/mail_template.py @@ -6,7 +6,11 @@ class MailTemplate(models.Model): @api.multi def send_mail_with_multiple_attachments( - self, res_id, additional_attachments, force_send=False, raise_exception=False + 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`` 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 c3c04fa..c31116b 100644 --- a/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py +++ b/easy_my_coop_taxshelter_report/models/tax_shelter_declaration.py @@ -38,10 +38,16 @@ class TaxShelterDeclaration(models.Model): 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 + [("30", "30%"), ("45", "45%")], + string="Tax Shelter percentage", + required=True, ) state = fields.Selection( - [("draft", "Draft"), ("computed", "Computed"), ("validated", "Validated")], + [ + ("draft", "Draft"), + ("computed", "Computed"), + ("validated", "Validated"), + ], string="State", required=True, default="draft", @@ -76,7 +82,10 @@ class TaxShelterDeclaration(models.Model): declaration = self else: declaration = self.search( - [("date_from", "<=", entry.date), ("date_to", ">=", entry.date)] + [ + ("date_from", "<=", entry.date), + ("date_to", ">=", entry.date), + ] ) if entry.partner_id.id in declaration.excluded_cooperator.ids: return True @@ -93,13 +102,18 @@ class TaxShelterDeclaration(models.Model): line_vals["type"] = TYPE_MAP[entry.type] if entry.type == "subscription": if not excluded: - capital_after_sub = ongoing_capital_sub + entry.total_amount_line + 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: + if ( + ongoing_capital_sub < self.tax_shelter_capital_limit + and not excluded + ): line_vals["tax_shelter"] = True return line_vals @@ -116,7 +130,9 @@ class TaxShelterDeclaration(models.Model): cert_vals[ "cooperator_number" ] = entry.partner_id.cooperator_register_number - certificate = self.env["tax.shelter.certificate"].create(cert_vals) + 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( @@ -129,8 +145,9 @@ class TaxShelterDeclaration(models.Model): return partner_certificate - @api.one + @api.multi def compute_declaration(self): + self.ensure_one() entries = self.env["subscription.register"].search( [ ("partner_id.is_company", "=", False), @@ -140,7 +157,7 @@ class TaxShelterDeclaration(models.Model): ) subscriptions = entries.filtered( - (lambda r: r.type == "subscription" and r.date < self.date_from) + lambda r: r.type == "subscription" and r.date < self.date_from ) # noqa cap_prev_sub = 0.0 for subscription in subscriptions: @@ -154,13 +171,15 @@ class TaxShelterDeclaration(models.Model): self.state = "computed" - @api.one + @api.multi def validate_declaration(self): + self.ensure_one() self.tax_shelter_certificates.write({"state": "validated"}) self.state = "validated" - @api.one + @api.multi def reset_declaration(self): + self.ensure_one() if not self.state == "validated": self.tax_shelter_certificates.unlink() self.state = "draft" @@ -235,13 +254,15 @@ class TaxShelterCertificate(models.Model): compute="_compute_amounts", string="Total previously subscribed" ) total_amount_eligible_previously_subscribed = fields.Float( - compute="_compute_amounts", string="Total eligible previously subscribed" + 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" + compute="_compute_amounts", + string="Total amount eligible To Tax shelter", ) total_amount_resold = fields.Float( compute="_compute_amounts", string="Total resold" @@ -252,14 +273,21 @@ class TaxShelterCertificate(models.Model): total_amount = fields.Float( compute="_compute_amounts", string="Total", readonly=True ) - company_id = fields.Many2one(related="declaration_id.company_id", string="Company") + 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.id)[0] report = base64.b64encode(report) report_name = ( - self.partner_id.name + " " + name + " " + self.declaration_id.name + ".pdf" + self.partner_id.name + + " " + + name + + " " + + self.declaration_id.name + + ".pdf" ) return (report_name, report) @@ -325,7 +353,9 @@ class TaxShelterCertificate(models.Model): certificate.total_amount_eligible = total_amount_elligible for line in certificate.previously_subscribed_eligible_lines: - total_amount_previously_eligible += line.amount_subscribed_eligible + total_amount_previously_eligible += ( + line.amount_subscribed_eligible + ) certificate.total_amount_eligible_previously_subscribed = ( total_amount_previously_eligible ) @@ -357,7 +387,7 @@ class TaxShelterCertificate(models.Model): lambda r: r.type == "subscribed" and r.transaction_date < certificate.declaration_id.date_from ) - certificate.previously_subscribed_eligible_lines = certificate.lines.filtered( + certificate.previously_subscribed_eligible_lines = certificate.lines.filtered( # noqa lambda r: r.type == "subscribed" and r.transaction_date < certificate.declaration_id.date_from and r.tax_shelter @@ -401,8 +431,12 @@ class TaxShelterCertificateLine(models.Model): 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) + 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( @@ -419,7 +453,9 @@ class TaxShelterCertificateLine(models.Model): compute="_compute_totals", string="Amount subscribed", store=True ) amount_subscribed_eligible = fields.Float( - compute="_compute_totals", string="Amount subscribed eligible", store=True + compute="_compute_totals", + string="Amount subscribed eligible", + store=True, ) amount_resold = fields.Float( compute="_compute_totals", string="Amount resold", store=True @@ -431,7 +467,9 @@ class TaxShelterCertificateLine(models.Model): capital_before_sub = fields.Float( string="Capital before subscription", readonly=True ) - capital_after_sub = fields.Float(string="Capital after 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 @@ -460,4 +498,6 @@ class TaxShelterCertificateLine(models.Model): 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) + line.amount_transfered = line.share_unit_price * -( + line.quantity + ) diff --git a/easy_my_coop_website/__manifest__.py b/easy_my_coop_website/__manifest__.py index 0067100..5f5dbbf 100644 --- a/easy_my_coop_website/__manifest__.py +++ b/easy_my_coop_website/__manifest__.py @@ -6,23 +6,19 @@ { "name": "Easy My Coop Website", "version": "12.0.1.0.0", - "depends": [ - "easy_my_coop", - "website", - "website_recaptcha_reloaded", - ], + "depends": ["easy_my_coop", "website", "website_recaptcha_reloaded"], "author": "Coop IT Easy SCRLfs", "category": "Cooperative management", "website": "www.coopiteasy.be", "license": "AGPL-3", - "description": """ - This module adds the cooperator subscription form allowing to subscribe for - shares online. + "summary": """ + This module adds the cooperator subscription form + allowing to subscribe for shares online. """, - 'data': [ - 'views/subscription_template.xml', - 'data/website_cooperator_data.xml', + "data": [ + "views/subscription_template.xml", + "data/website_cooperator_data.xml", ], - 'installable': True, - 'application': True, + "installable": True, + "application": True, } diff --git a/easy_my_coop_website/controllers/main.py b/easy_my_coop_website/controllers/main.py index 1a51c46..60bff75 100644 --- a/easy_my_coop_website/controllers/main.py +++ b/easy_my_coop_website/controllers/main.py @@ -1,39 +1,79 @@ import base64 -from datetime import datetime import re +from datetime import datetime from odoo import http from odoo.http import request from odoo.tools.translate import _ # Only use for behavior, don't stock it -_TECHNICAL = ['view_from', 'view_callback'] +_TECHNICAL = ["view_from", "view_callback"] # Allow in description -_BLACKLIST = ['id', 'create_uid', 'create_date', 'write_uid', 'write_date', - 'user_id', 'active'] - -_COOP_FORM_FIELD = ['email', 'confirm_email', 'firstname', 'lastname', - 'birthdate', 'iban', 'share_product_id', - 'address', 'city', 'zip_code', 'country_id', 'phone', - 'lang', 'nb_parts', 'total_parts', 'error_msg'] - -_COMPANY_FORM_FIELD = ['is_company', 'company_register_number', 'company_name', - 'company_email', 'confirm_email', 'email', 'firstname', - 'lastname', 'birthdate', 'iban', 'share_product_id', - 'address', 'city', 'zip_code', 'country_id', 'phone', - 'lang', 'nb_parts', 'total_parts', 'error_msg', - 'company_type'] +_BLACKLIST = [ + "id", + "create_uid", + "create_date", + "write_uid", + "write_date", + "user_id", + "active", +] + +_COOP_FORM_FIELD = [ + "email", + "confirm_email", + "firstname", + "lastname", + "birthdate", + "iban", + "share_product_id", + "address", + "city", + "zip_code", + "country_id", + "phone", + "lang", + "nb_parts", + "total_parts", + "error_msg", +] + +_COMPANY_FORM_FIELD = [ + "is_company", + "company_register_number", + "company_name", + "company_email", + "confirm_email", + "email", + "firstname", + "lastname", + "birthdate", + "iban", + "share_product_id", + "address", + "city", + "zip_code", + "country_id", + "phone", + "lang", + "nb_parts", + "total_parts", + "error_msg", + "company_type", +] class WebsiteSubscription(http.Controller): - - @http.route(['/page/become_cooperator', - '/become_cooperator'], - type='http', auth="public", website=True) + @http.route( + ["/page/become_cooperator", "/become_cooperator"], + type="http", + auth="public", + website=True, + ) def display_become_cooperator_page(self, **kwargs): values = {} logged = False - if request.env.user.login != 'public': + if request.env.user.login != "public": logged = True partner = request.env.user.partner_id if partner.is_company: @@ -47,14 +87,17 @@ class WebsiteSubscription(http.Controller): values.update(kwargs=kwargs.items()) return request.render("easy_my_coop_website.becomecooperator", values) - @http.route(['/page/become_company_cooperator', - '/become_company_cooperator'], - type='http', auth="public", website=True) + @http.route( + ["/page/become_company_cooperator", "/become_company_cooperator"], + type="http", + auth="public", + website=True, + ) def display_become_company_cooperator_page(self, **kwargs): values = {} logged = False - if request.env.user.login != 'public': + if request.env.user.login != "public": logged = True values = self.fill_values(values, True, logged, True) @@ -62,15 +105,13 @@ class WebsiteSubscription(http.Controller): if kwargs.get(field): values[field] = kwargs.pop(field) values.update(kwargs=kwargs.items()) - return request.render("easy_my_coop_website.becomecompanycooperator", - values) + return request.render( + "easy_my_coop_website.becomecompanycooperator", values + ) def preRenderThanks(self, values, kwargs): """ Allow to be overrided """ - return { - '_values': values, - '_kwargs': kwargs, - } + return {"_values": values, "_kwargs": kwargs} def get_subscription_response(self, values, kwargs): values = self.preRenderThanks(values, kwargs) @@ -83,175 +124,198 @@ class WebsiteSubscription(http.Controller): def get_values_from_user(self, values, is_company): # the subscriber is connected - if request.env.user.login != 'public': - values['logged'] = 'on' + if request.env.user.login != "public": + values["logged"] = "on" partner = request.env.user.partner_id if partner.member or partner.old_member: - values['already_cooperator'] = 'on' + values["already_cooperator"] = "on" if partner.bank_ids: - values['iban'] = partner.bank_ids[0].acc_number - values['address'] = partner.street - values['zip_code'] = partner.zip - values['city'] = partner.city - values['country_id'] = partner.country_id.id + values["iban"] = partner.bank_ids[0].acc_number + values["address"] = partner.street + values["zip_code"] = partner.zip + values["city"] = partner.city + values["country_id"] = partner.country_id.id if is_company: # company values - values['company_register_number'] = partner.company_register_number - values['company_name'] = partner.name - values['company_email'] = partner.email - values['company_type'] = partner.legal_form + values[ + "company_register_number" + ] = partner.company_register_number + values["company_name"] = partner.name + values["company_email"] = partner.email + values["company_type"] = partner.legal_form # contact person values representative = partner.get_representative() - values['firstname'] = representative.firstname - values['lastname'] = representative.lastname - values['gender'] = representative.gender - values['email'] = representative.email - values['contact_person_function'] = representative.function - values['birthdate'] = self.get_date_string(representative.birthdate_date) - values['lang'] = representative.lang - values['phone'] = representative.phone + values["firstname"] = representative.firstname + values["lastname"] = representative.lastname + values["gender"] = representative.gender + values["email"] = representative.email + values["contact_person_function"] = representative.function + values["birthdate"] = self.get_date_string( + representative.birthdate_date + ) + values["lang"] = representative.lang + values["phone"] = representative.phone else: - values['firstname'] = partner.firstname - values['lastname'] = partner.lastname - values['email'] = partner.email - values['gender'] = partner.gender - values['birthdate'] = self.get_date_string(partner.birthdate_date) - values['lang'] = partner.lang - values['phone'] = partner.phone + values["firstname"] = partner.firstname + values["lastname"] = partner.lastname + values["email"] = partner.email + values["gender"] = partner.gender + values["birthdate"] = self.get_date_string( + partner.birthdate_date + ) + values["lang"] = partner.lang + values["phone"] = partner.phone return values def fill_values(self, values, is_company, logged, load_from_user=False): - sub_req_obj = request.env['subscription.request'] + sub_req_obj = request.env["subscription.request"] company = request.website.company_id products = self.get_products_share(is_company) if load_from_user: values = self.get_values_from_user(values, is_company) if is_company: - values['is_company'] = 'on' + values["is_company"] = "on" if logged: - values['logged'] = 'on' - values['countries'] = self.get_countries() - values['langs'] = self.get_langs() - values['products'] = products - fields_desc = sub_req_obj.sudo().fields_get(['company_type', 'gender']) - values['company_types'] = fields_desc['company_type']['selection'] - values['genders'] = fields_desc['gender']['selection'] - values['company'] = company - - if not values.get('share_product_id'): + values["logged"] = "on" + values["countries"] = self.get_countries() + values["langs"] = self.get_langs() + values["products"] = products + fields_desc = sub_req_obj.sudo().fields_get(["company_type", "gender"]) + values["company_types"] = fields_desc["company_type"]["selection"] + values["genders"] = fields_desc["gender"]["selection"] + values["company"] = company + + if not values.get("share_product_id"): for product in products: if product.default_share_product is True: - values['share_product_id'] = product.id + values["share_product_id"] = product.id break - if not values.get('share_product_id', False) and products: - values['share_product_id'] = products[0].id - if not values.get('country_id'): + if not values.get("share_product_id", False) and products: + values["share_product_id"] = products[0].id + if not values.get("country_id"): if company.default_country_id: - values['country_id'] = company.default_country_id.id + values["country_id"] = company.default_country_id.id else: - values['country_id'] = '21' - if not values.get('activities_country_id'): + values["country_id"] = "21" + if not values.get("activities_country_id"): if company.default_country_id: - values['activities_country_id'] = company.default_country_id.id + values["activities_country_id"] = company.default_country_id.id else: - values['activities_country_id'] = '21' - if not values.get('lang'): + values["activities_country_id"] = "21" + if not values.get("lang"): if company.default_lang_id: - values['lang'] = company.default_lang_id.code - - comp = request.env['res.company']._company_default_get() - values.update({ - 'display_data_policy': comp.display_data_policy_approval, - 'data_policy_required': comp.data_policy_approval_required, - 'data_policy_text': comp.data_policy_approval_text, - 'display_internal_rules': comp.display_internal_rules_approval, - 'internal_rules_required': comp.internal_rules_approval_required, - 'internal_rules_text': comp.internal_rules_approval_text, - }) + values["lang"] = company.default_lang_id.code + + comp = request.env["res.company"]._company_default_get() + values.update( + { + "display_data_policy": comp.display_data_policy_approval, + "data_policy_required": comp.data_policy_approval_required, + "data_policy_text": comp.data_policy_approval_text, + "display_internal_rules": comp.display_internal_rules_approval, + "internal_rules_required": comp.internal_rules_approval_required, + "internal_rules_text": comp.internal_rules_approval_text, + } + ) return values def get_products_share(self, is_company): - product_obj = request.env['product.template'] + product_obj = request.env["product.template"] products = product_obj.sudo().get_web_share_products(is_company) return products def get_countries(self): - countries = request.env['res.country'].sudo().search([]) + countries = request.env["res.country"].sudo().search([]) return countries def get_langs(self): - langs = request.env['res.lang'].sudo().search([]) + langs = request.env["res.lang"].sudo().search([]) return langs def get_selected_share(self, kwargs): - prod_obj = request.env['product.template'] + prod_obj = request.env["product.template"] product_id = kwargs.get("share_product_id") return prod_obj.sudo().browse(int(product_id)).product_variant_ids[0] def validation(self, kwargs, logged, values, post_file): - user_obj = request.env['res.users'] - sub_req_obj = request.env['subscription.request'] + user_obj = request.env["res.users"] + sub_req_obj = request.env["subscription.request"] redirect = "easy_my_coop_website.becomecooperator" - email = kwargs.get('email') - is_company = kwargs.get("is_company") == 'on' + email = kwargs.get("email") + is_company = kwargs.get("is_company") == "on" if is_company: is_company = True redirect = "easy_my_coop_website.becomecompanycooperator" - email = kwargs.get('company_email') + email = kwargs.get("company_email") - if 'g-recaptcha-response' not in kwargs or kwargs['g-recaptcha-response'] == '': + if ( + "g-recaptcha-response" not in kwargs + or kwargs["g-recaptcha-response"] == "" + ): values = self.fill_values(values, is_company, logged) values.update(kwargs) - values["error_msg"] = _("the captcha has not been validated," - " please fill in the captcha") + values["error_msg"] = _( + "the captcha has not been validated," + " please fill in the captcha" + ) return request.render(redirect, values) elif not request.website.is_captcha_valid( - kwargs['g-recaptcha-response']): + kwargs["g-recaptcha-response"] + ): values = self.fill_values(values, is_company, logged) values.update(kwargs) - values["error_msg"] = _("the captcha has not been validated," - " please fill in the captcha") + values["error_msg"] = _( + "the captcha has not been validated," + " please fill in the captcha" + ) return request.render(redirect, values) # Check that required field from model subscription_request exists required_fields = sub_req_obj.sudo().get_required_field() - error = set(field for field in required_fields if not values.get(field)) #noqa + error = { + field for field in required_fields if not values.get(field) + } # noqa if error: values = self.fill_values(values, is_company, logged) - values["error_msg"] = _("Some mandatory fields have not " - "been filled") + values["error_msg"] = _( + "Some mandatory fields have not " "been filled" + ) values = dict(values, error=error, kwargs=kwargs.items()) return request.render(redirect, values) if not logged and email: - user = user_obj.sudo().search([('login', '=', email)]) + user = user_obj.sudo().search([("login", "=", email)]) if user: values = self.fill_values(values, is_company, logged) values.update(kwargs) - values["error_msg"] = _("There is an existing account for this" - " mail address. Please login before " - "fill in the form") + values["error_msg"] = _( + "There is an existing account for this" + " mail address. Please login before " + "fill in the form" + ) return request.render(redirect, values) else: - confirm_email = kwargs.get('confirm_email') + confirm_email = kwargs.get("confirm_email") if email != confirm_email: values = self.fill_values(values, is_company, logged) values.update(kwargs) - values["error_msg"] = _("The email and the confirmation " - "email doesn't match.Please check " - "the given mail addresses") + values["error_msg"] = _( + "The email and the confirmation " + "email doesn't match.Please check " + "the given mail addresses" + ) return request.render(redirect, values) company = request.website.company_id @@ -259,8 +323,9 @@ class WebsiteSubscription(http.Controller): if not post_file: values = self.fill_values(values, is_company, logged) values.update(kwargs) - values["error_msg"] = _("You need to upload a" - " scan of your id card") + values["error_msg"] = _( + "You need to upload a" " scan of your id card" + ) return request.render(redirect, values) iban = kwargs.get("iban") @@ -268,8 +333,7 @@ class WebsiteSubscription(http.Controller): if not valid: values = self.fill_values(values, is_company, logged) - values["error_msg"] = _("You iban account number" - "is not valid") + values["error_msg"] = _("You iban account number" "is not valid") return request.render(redirect, values) # check the subscription's amount @@ -282,41 +346,50 @@ class WebsiteSubscription(http.Controller): share = self.get_selected_share(kwargs) if partner.cooperator_type != share.default_code: values = self.fill_values(values, is_company, logged) - values["error_msg"] = (_("You can't subscribe two " - "different types of share")) + values["error_msg"] = _( + "You can't subscribe two " + "different types of share" + ) return request.render(redirect, values) - total_amount = float(kwargs.get('total_parts')) + total_amount = float(kwargs.get("total_parts")) if max_amount > 0 and total_amount > max_amount: values = self.fill_values(values, is_company, logged) - values["error_msg"] = (_("You can't subscribe for an amount that " - "exceed ") - + str(max_amount) - + company.currency_id.symbol) + values["error_msg"] = ( + _("You can't subscribe for an amount that " "exceed ") + + str(max_amount) + + company.currency_id.symbol + ) return request.render(redirect, values) return True - @http.route(['/subscription/get_share_product'], - type='json', - auth="public", - methods=['POST'], website=True) + @http.route( + ["/subscription/get_share_product"], + type="json", + auth="public", + methods=["POST"], + website=True, + ) def get_share_product(self, share_product_id, **kw): - product_template = request.env['product.template'] + product_template = request.env["product.template"] product = product_template.sudo().browse(int(share_product_id)) return { product.id: { - 'list_price': product.list_price, - 'min_qty': product.minimum_quantity, - 'force_min_qty': product.force_min_qty - } + "list_price": product.list_price, + "min_qty": product.minimum_quantity, + "force_min_qty": product.force_min_qty, } + } - @http.route(['/subscription/subscribe_share'], - type='http', - auth="public", website=True) + @http.route( + ["/subscription/subscribe_share"], + type="http", + auth="public", + website=True, + ) def share_subscription(self, **kwargs): - sub_req_obj = request.env['subscription.request'] - attach_obj = request.env['ir.attachment'] + sub_req_obj = request.env["subscription.request"] + attach_obj = request.env["ir.attachment"] # List of file to add to ir_attachment once we have the ID post_file = [] @@ -325,17 +398,21 @@ class WebsiteSubscription(http.Controller): values = {} for field_name, field_value in kwargs.items(): - if hasattr(field_value, 'filename'): + if hasattr(field_value, "filename"): post_file.append(field_value) - elif (field_name in sub_req_obj._fields - and field_name not in _BLACKLIST): + elif ( + field_name in sub_req_obj._fields + and field_name not in _BLACKLIST + ): values[field_name] = field_value # allow to add some free fields or blacklisted field like ID elif field_name not in _TECHNICAL: - post_description.append("%s: %s" % (field_name, field_value)) + post_description.append( + "{}: {}".format(field_name, field_value) + ) - logged = kwargs.get("logged") == 'on' - is_company = kwargs.get("is_company") == 'on' + logged = kwargs.get("logged") == "on" + is_company = kwargs.get("is_company") == "on" response = self.validation(kwargs, logged, values, post_file) if response is not True: @@ -344,19 +421,19 @@ class WebsiteSubscription(http.Controller): already_coop = False if logged: partner = request.env.user.partner_id - values['partner_id'] = partner.id + values["partner_id"] = partner.id already_coop = partner.member - elif kwargs.get("already_cooperator") == 'on': + elif kwargs.get("already_cooperator") == "on": already_coop = True values["already_cooperator"] = already_coop values["is_company"] = is_company - if kwargs.get('data_policy_approved', 'off') == 'on': - values['data_policy_approved'] = True + if kwargs.get("data_policy_approved", "off") == "on": + values["data_policy_approved"] = True - if kwargs.get('internal_rules_approved', 'off') == 'on': - values['internal_rules_approved'] = True + if kwargs.get("internal_rules_approved", "off") == "on": + values["internal_rules_approved"] = True lastname = kwargs.get("lastname").upper() firstname = kwargs.get("firstname").title() @@ -364,17 +441,18 @@ class WebsiteSubscription(http.Controller): values["name"] = firstname + " " + lastname values["lastname"] = lastname values["firstname"] = firstname - values["birthdate"] = datetime.strptime(kwargs.get("birthdate"), - "%d/%m/%Y").date() + values["birthdate"] = datetime.strptime( + kwargs.get("birthdate"), "%d/%m/%Y" + ).date() values["source"] = "website" values["share_product_id"] = self.get_selected_share(kwargs).id if is_company: if kwargs.get("company_register_number", is_company): - values["company_register_number"] = re.sub('[^0-9a-zA-Z]+', - '', - kwargs.get("company_register_number")) + values["company_register_number"] = re.sub( + "[^0-9a-zA-Z]+", "", kwargs.get("company_register_number") + ) subscription_id = sub_req_obj.sudo().create_comp_sub_req(values) else: subscription_id = sub_req_obj.sudo().create(values) @@ -382,12 +460,12 @@ class WebsiteSubscription(http.Controller): if subscription_id: for field_value in post_file: attachment_value = { - 'name': field_value.filename, - 'res_name': field_value.filename, - 'res_model': 'subscription.request', - 'res_id': subscription_id, - 'datas': base64.encodestring(field_value.read()), - 'datas_fname': field_value.filename, + "name": field_value.filename, + "res_name": field_value.filename, + "res_model": "subscription.request", + "res_id": subscription_id, + "datas": base64.encodestring(field_value.read()), + "datas_fname": field_value.filename, } attach_obj.sudo().create(attachment_value) diff --git a/easy_my_coop_website_portal/__manifest__.py b/easy_my_coop_website_portal/__manifest__.py index 95345cf..f7da2c6 100644 --- a/easy_my_coop_website_portal/__manifest__.py +++ b/easy_my_coop_website_portal/__manifest__.py @@ -3,23 +3,16 @@ # - Houssine Bakkali # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Easy My Coop Website Portal', + "name": "Easy My Coop Website Portal", "version": "12.0.1.0.0", - 'depends': [ - 'easy_my_coop', - 'website', - 'account', - 'portal', - ], - 'description': """ + "depends": ["easy_my_coop", "website", "account", "portal"], + "summary": """ Show cooperator information in the website portal. """, - 'author': 'Coop IT Easy SCRLfs', - 'license': 'AGPL-3', - 'category': 'Cooperative Management', + "author": "Coop IT Easy SCRLfs", + "license": "AGPL-3", + "category": "Cooperative Management", "website": "www.coopiteasy.be", - 'data': [ - 'views/easy_my_coop_website_portal_templates.xml', - ], - 'installable': True, + "data": ["views/easy_my_coop_website_portal_templates.xml"], + "installable": True, } diff --git a/easy_my_coop_website_portal/controllers/main.py b/easy_my_coop_website_portal/controllers/main.py index 1d2b2b4..841a343 100644 --- a/easy_my_coop_website_portal/controllers/main.py +++ b/easy_my_coop_website_portal/controllers/main.py @@ -4,211 +4,263 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _ from odoo.exceptions import AccessError, MissingError from odoo.fields import Date from odoo.http import request, route -from odoo import _ -from odoo.addons.portal.controllers.portal import CustomerPortal -from odoo.addons.portal.controllers.portal import pager as portal_pager from odoo.addons.payment.controllers.portal import PaymentProcessing +from odoo.addons.portal.controllers.portal import ( + CustomerPortal, + pager as portal_pager, +) class CooperatorPortalAccount(CustomerPortal): - CustomerPortal.MANDATORY_BILLING_FIELDS.extend(["iban", - "birthdate_date", - "gender"]) + CustomerPortal.MANDATORY_BILLING_FIELDS.extend( + ["iban", "birthdate_date", "gender"] + ) def _prepare_portal_layout_values(self): - values = super(CooperatorPortalAccount, - self)._prepare_portal_layout_values() + values = super( + CooperatorPortalAccount, self + )._prepare_portal_layout_values() # We assume that commercial_partner_id always point to the # partner itself or to the linked partner. So there is no # need to check if the partner is a "contact" or not. partner = request.env.user.partner_id coop = partner.commercial_partner_id - partner_obj = request.env['res.partner'] - coop_bank = request.env['res.partner.bank'].sudo().search( - [('partner_id', 'in', [coop.id])], - limit=1 + partner_obj = request.env["res.partner"] + coop_bank = ( + request.env["res.partner.bank"] + .sudo() + .search([("partner_id", "in", [coop.id])], limit=1) + ) + invoice_mgr = request.env["account.invoice"] + capital_request_count = invoice_mgr.search_count( + [ + ("state", "in", ["open", "paid", "cancelled"]), + # Get only the release capital request + ("release_capital_request", "=", True), + ] ) - invoice_mgr = request.env['account.invoice'] - capital_request_count = invoice_mgr.search_count([ - ('state', 'in', ['open', 'paid', 'cancelled']), - # Get only the release capital request - ('release_capital_request', '=', True), - ]) - invoice_count = invoice_mgr.search_count([ - ('release_capital_request', '=', False) - ]) - iban = '' + invoice_count = invoice_mgr.search_count( + [("release_capital_request", "=", False)] + ) + iban = "" if partner.bank_ids: iban = partner.bank_ids[0].acc_number - fields_desc = partner_obj.sudo().fields_get(['gender']) + fields_desc = partner_obj.sudo().fields_get(["gender"]) - values.update({ - 'coop': coop, - 'coop_bank': coop_bank, - 'capital_request_count': capital_request_count, - 'invoice_count': invoice_count, - 'iban': iban, - 'genders': fields_desc['gender']['selection'] - }) + values.update( + { + "coop": coop, + "coop_bank": coop_bank, + "capital_request_count": capital_request_count, + "invoice_count": invoice_count, + "iban": iban, + "genders": fields_desc["gender"]["selection"], + } + ) return values def details_form_validate(self, data): - error, error_message = super(CooperatorPortalAccount, - self).details_form_validate(data) - sub_req_obj = request.env['subscription.request'] + error, error_message = super( + CooperatorPortalAccount, self + ).details_form_validate(data) + sub_req_obj = request.env["subscription.request"] iban = data.get("iban") valid = sub_req_obj.check_iban(iban) if not valid: - error['iban'] = 'error' + error["iban"] = "error" error_message.append(_("You iban account number is not valid")) return error, error_message - @route(['/my/account'], type='http', auth='user', website=True) + @route(["/my/account"], type="http", auth="user", website=True) def account(self, redirect=None, **post): - res = super(CooperatorPortalAccount, self).account( - redirect, **post) - if not res.qcontext.get('error'): + res = super(CooperatorPortalAccount, self).account(redirect, **post) + if not res.qcontext.get("error"): partner = request.env.user.partner_id - partner_bank = request.env['res.partner.bank'] - iban = post.get('iban') + partner_bank = request.env["res.partner.bank"] + iban = post.get("iban") if iban: if partner.bank_ids: bank_account = partner.bank_ids[0] bank_account.acc_number = iban else: - partner_bank.sudo().create({ - 'partner_id': partner.id, - 'acc_number': iban - }) + partner_bank.sudo().create( + {"partner_id": partner.id, "acc_number": iban} + ) return res - @route(['/my/invoices', '/my/invoices/page/'], type='http', - auth="user", website=True) - def portal_my_invoices(self, page=1, date_begin=None, date_end=None, - sortby=None, **kw): + @route( + ["/my/invoices", "/my/invoices/page/"], + type="http", + auth="user", + website=True, + ) + def portal_my_invoices( + self, page=1, date_begin=None, date_end=None, sortby=None, **kw + ): res = super(CooperatorPortalAccount, self).portal_my_invoices( - page, date_begin, date_end, sortby, **kw) - invoice_obj = request.env['account.invoice'] + page, date_begin, date_end, sortby, **kw + ) + invoice_obj = request.env["account.invoice"] qcontext = res.qcontext if qcontext: - invoices = invoice_obj.search([('release_capital_request', '=', False)]) + invoices = invoice_obj.search( + [("release_capital_request", "=", False)] + ) invoice_count = len(invoices) - qcontext['invoices'] = invoices - qcontext['pager']['invoice_count'] = invoice_count + qcontext["invoices"] = invoices + qcontext["pager"]["invoice_count"] = invoice_count return res @route( - ['/my/release_capital_request', - '/my/release_capital_request/page/'], - type='http', auth="user", website=True) - def portal_my_release_capital_request(self, page=1, date_begin=None, - date_end=None, sortby=None, **kw): + [ + "/my/release_capital_request", + "/my/release_capital_request/page/", + ], + type="http", + auth="user", + website=True, + ) + def portal_my_release_capital_request( + self, page=1, date_begin=None, date_end=None, sortby=None, **kw + ): """Render a page with the list of release capital request. A release capital request is an invoice with a flag that tell if it's a capital request or not. """ values = self._prepare_portal_layout_values() partner = request.env.user.partner_id - invoice_mgr = request.env['account.invoice'] + invoice_mgr = request.env["account.invoice"] domain = [ - ('partner_id', 'in', - [partner.commercial_partner_id.id]), - ('state', 'in', ['open', 'paid', 'cancelled']), + ("partner_id", "in", [partner.commercial_partner_id.id]), + ("state", "in", ["open", "paid", "cancelled"]), # Get only the release capital request - ('release_capital_request', '=', True), + ("release_capital_request", "=", True), ] - archive_groups = self._get_archive_groups_sudo('account.invoice', - domain) + archive_groups = self._get_archive_groups_sudo( + "account.invoice", domain + ) if date_begin and date_end: - domain += [('create_date', '>=', date_begin), - ('create_date', '<', date_end)] + domain += [ + ("create_date", ">=", date_begin), + ("create_date", "<", date_end), + ] # count for pager capital_request_count = invoice_mgr.sudo().search_count(domain) # pager pager = portal_pager( url="/my/release_capital_request", - url_args={'date_begin': date_begin, 'date_end': date_end, - 'sortby': sortby}, + url_args={ + "date_begin": date_begin, + "date_end": date_end, + "sortby": sortby, + }, total=capital_request_count, page=page, - step=self._items_per_page + step=self._items_per_page, ) # content according to pager and archive selected invoices = invoice_mgr.sudo().search( - domain, limit=self._items_per_page, offset=pager['offset']) - values.update({ - 'date': date_begin, - 'capital_requests': invoices, - 'page_name': 'Release request', - 'pager': pager, - 'archive_groups': archive_groups, - 'default_url': '/my/release_capital_request', - }) + domain, limit=self._items_per_page, offset=pager["offset"] + ) + values.update( + { + "date": date_begin, + "capital_requests": invoices, + "page_name": "Release request", + "pager": pager, + "archive_groups": archive_groups, + "default_url": "/my/release_capital_request", + } + ) return request.render( - "easy_my_coop_website_portal.portal_my_capital_releases", - values + "easy_my_coop_website_portal.portal_my_capital_releases", values ) - @route(['/my/invoices/'], - type='http', auth="public", website=True) - def portal_my_invoice_detail(self, invoice_id, access_token=None, - report_type=None, download=False, **kw): + @route( + ["/my/invoices/"], + type="http", + auth="public", + website=True, + ) + def portal_my_invoice_detail( + self, + invoice_id, + access_token=None, + report_type=None, + download=False, + **kw + ): # override in order to not retrieve release capital request as invoices try: - invoice_sudo = self._document_check_access('account.invoice', - invoice_id, - access_token) + invoice_sudo = self._document_check_access( + "account.invoice", invoice_id, access_token + ) except (AccessError, MissingError): - return request.redirect('/my') + return request.redirect("/my") if invoice_sudo.release_capital_request: - report_ref = 'easy_my_coop.action_cooperator_invoices' + report_ref = "easy_my_coop.action_cooperator_invoices" else: - report_ref = 'account.account_invoices' - if report_type in ('html', 'pdf', 'text'): - return self._show_report(model=invoice_sudo, - report_type=report_type, - report_ref=report_ref, - download=download) - - values = self._invoice_get_page_view_values(invoice_sudo, access_token, - **kw) - PaymentProcessing.remove_payment_transaction(invoice_sudo.transaction_ids) + report_ref = "account.account_invoices" + if report_type in ("html", "pdf", "text"): + return self._show_report( + model=invoice_sudo, + report_type=report_type, + report_ref=report_ref, + download=download, + ) + + values = self._invoice_get_page_view_values( + invoice_sudo, access_token, **kw + ) + PaymentProcessing.remove_payment_transaction( + invoice_sudo.transaction_ids + ) return request.render("account.portal_invoice_page", values) - @route(['/my/cooperator_certificate/pdf'], - type='http', auth="user", website=True) + @route( + ["/my/cooperator_certificate/pdf"], + type="http", + auth="user", + website=True, + ) def get_cooperator_certificat(self, **kw): """Render the cooperator certificate pdf of the current user""" partner = request.env.user.partner_id return self._show_report( - model=partner, - report_type='pdf', - report_ref='easy_my_coop.action_cooperator_report_certificat', - download=True - ) + model=partner, + report_type="pdf", + report_ref="easy_my_coop.action_cooperator_report_certificat", + download=True, + ) def _render_pdf(self, pdf, filename): """Render a http response for a pdf""" pdfhttpheaders = [ - ('Content-Disposition', 'inline; filename="%s.pdf"' % filename), - ('Content-Type', 'application/pdf'), - ('Content-Length', len(pdf)) + ("Content-Disposition", 'inline; filename="%s.pdf"' % filename), + ("Content-Type", "application/pdf"), + ("Content-Length", len(pdf)), ] return request.make_response(pdf, headers=pdfhttpheaders) - def _get_archive_groups_sudo(self, model, domain=None, fields=None, - groupby="create_date", - order="create_date desc"): + def _get_archive_groups_sudo( + self, + model, + domain=None, + fields=None, + groupby="create_date", + order="create_date desc", + ): """Same as the one from website_portal_v10 except that it runs in root. """ @@ -217,10 +269,13 @@ class CooperatorPortalAccount(CustomerPortal): if domain is None: domain = [] if fields is None: - fields = ['name', 'create_date'] + fields = ["name", "create_date"] groups = [] - for group in request.env[model].sudo().read_group( - domain, fields=fields, groupby=groupby, orderby=order): + for group in ( + request.env[model] + .sudo() + .read_group(domain, fields=fields, groupby=groupby, orderby=order) + ): label = group[groupby] date_begin = date_end = None for leaf in group["__domain"]: @@ -229,10 +284,12 @@ class CooperatorPortalAccount(CustomerPortal): date_begin = leaf[2] elif leaf[1] == "<": date_end = leaf[2] - groups.append({ - 'date_begin': Date.to_string(Date.from_string(date_begin)), - 'date_end': Date.to_string(Date.from_string(date_end)), - 'name': label, - 'item_count': group[groupby + '_count'] - }) + groups.append( + { + "date_begin": Date.to_string(Date.from_string(date_begin)), + "date_end": Date.to_string(Date.from_string(date_end)), + "name": label, + "item_count": group[groupby + "_count"], + } + ) return groups diff --git a/easy_my_coop_website_taxshelter/__manifest__.py b/easy_my_coop_website_taxshelter/__manifest__.py index 8e69982..68524e3 100644 --- a/easy_my_coop_website_taxshelter/__manifest__.py +++ b/easy_my_coop_website_taxshelter/__manifest__.py @@ -3,27 +3,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Easy My Coop Tax Shelter Website', + "name": "Easy My Coop Tax Shelter Website", "version": "12.0.1.0.0", - 'depends': [ - 'website', - 'website_portal_v10', - 'easy_my_coop_taxshelter_report', - 'report', + "depends": [ + "website", + "website_portal_v10", + "easy_my_coop_taxshelter_report", + "report", ], - 'description': """ - Give access to Tax Shelter Report in the website portal. - """, - 'author': 'Coop IT Easy SCRLfs', - 'license': 'AGPL-3', - 'category': 'Cooperative Management', + "summary": "Give access to Tax Shelter Report in the website portal.", + "author": "Coop IT Easy SCRLfs", + "license": "AGPL-3", + "category": "Cooperative Management", "website": "www.coopiteasy.be", - - 'category': 'Cooperative Management', - - 'data': [ - 'views/easy_my_coop_website_taxshelter_templates.xml', - ], - 'installable': False, - 'application': False, + "data": ["views/easy_my_coop_website_taxshelter_templates.xml"], + "installable": False, + "application": False, } diff --git a/easy_my_coop_website_taxshelter/controllers/main.py b/easy_my_coop_website_taxshelter/controllers/main.py index 91497ab..3cf47d8 100644 --- a/easy_my_coop_website_taxshelter/controllers/main.py +++ b/easy_my_coop_website_taxshelter/controllers/main.py @@ -1,89 +1,93 @@ -# -*- coding: utf-8 -*- - # Copyright 2017-2018 Coop IT Easy SCRLfs # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from werkzeug.exceptions import Forbidden, NotFound - from openerp import http +from openerp.addons.website_portal_v10.controllers.main import WebsiteAccount from openerp.exceptions import AccessError, MissingError from openerp.http import request - -from openerp.addons.website_portal_v10.controllers.main import WebsiteAccount +from werkzeug.exceptions import Forbidden, NotFound class TaxShelterWebsiteAccount(WebsiteAccount): - @http.route() def account(self): """ Add Tax Shelter Certificate to main account page """ response = super(TaxShelterWebsiteAccount, self).account() partner = request.env.user.partner_id - tax_shelter_mgr = request.env['tax.shelter.certificate'] - tax_shelter_count = tax_shelter_mgr.sudo().search_count([ - ('partner_id', 'in', [partner.commercial_partner_id.id]), - ]) + tax_shelter_mgr = request.env["tax.shelter.certificate"] + tax_shelter_count = tax_shelter_mgr.sudo().search_count( + [("partner_id", "in", [partner.commercial_partner_id.id])] + ) - response.qcontext.update({ - 'tax_shelter_count': tax_shelter_count, - }) + response.qcontext.update({"tax_shelter_count": tax_shelter_count}) return response @http.route( - ['/my/tax_shelter_certificate', - '/my/tax_shelter_certificate/page/'], - type='http', auth="user", website=True) - def portal_my_tax_shelter_certificate(self, page=1, date_begin=None, - date_end=None, **kw): + [ + "/my/tax_shelter_certificate", + "/my/tax_shelter_certificate/page/", + ], + type="http", + auth="user", + website=True, + ) + def portal_my_tax_shelter_certificate( + self, page=1, date_begin=None, date_end=None, **kw + ): """Render a page that lits the tax shelter report: * Tax Shelter Certificates * Shares Certifcates """ values = self._prepare_portal_layout_values() partner = request.env.user.partner_id - tax_shelter_mgr = request.env['tax.shelter.certificate'] + tax_shelter_mgr = request.env["tax.shelter.certificate"] - domain = [ - ('partner_id', 'in', [partner.commercial_partner_id.id]), - ] + domain = [("partner_id", "in", [partner.commercial_partner_id.id])] if date_begin and date_end: - domain += [('create_date', '>=', date_begin), - ('create_date', '<', date_end)] + domain += [ + ("create_date", ">=", date_begin), + ("create_date", "<", date_end), + ] # count for pager tax_shelter_count = tax_shelter_mgr.sudo().search_count(domain) # pager pager = request.website.pager( url="/my/tax_shelter_certificate", - url_args={'date_begin': date_begin, 'date_end': date_end}, + url_args={"date_begin": date_begin, "date_end": date_end}, total=tax_shelter_count, page=page, - step=self._items_per_page + step=self._items_per_page, ) # content according to pager and archive selected tax_shelters = tax_shelter_mgr.sudo().search( - domain, limit=self._items_per_page, offset=pager['offset']) + domain, limit=self._items_per_page, offset=pager["offset"] + ) tax_shelters = tax_shelters.sorted( - key=lambda r: r.declaration_id.fiscal_year, - reverse=True + key=lambda r: r.declaration_id.fiscal_year, reverse=True + ) + values.update( + { + "date": date_begin, + "tax_shelters": tax_shelters, + "page_name": "invoice", + "pager": pager, + "default_url": "/my/tax_shelter_certificate", + } ) - values.update({ - 'date': date_begin, - 'tax_shelters': tax_shelters, - 'page_name': 'invoice', - 'pager': pager, - 'default_url': '/my/tax_shelter_certificate', - }) return request.website.render( - "easy_my_coop_website_taxshelter.portal_my_tax_shelter", - values + "easy_my_coop_website_taxshelter.portal_my_tax_shelter", values ) - @http.route(['/my/taxshelter_certificate/pdf/'], - type='http', auth="user", website=True) + @http.route( + ["/my/taxshelter_certificate/pdf/"], + type="http", + auth="user", + website=True, + ) def get_taxshelter_certificate_pdf(self, oid=-1): """Render the Tax Shelter Certificate pdf of the given Tax Shelter Report @@ -91,7 +95,7 @@ class TaxShelterWebsiteAccount(WebsiteAccount): # Get the subscription certificate and raise an error if the user # is not allowed to access to it or if the object is not found. partner = request.env.user.partner_id - tax_shelter_mgr = request.env['tax.shelter.certificate'] + tax_shelter_mgr = request.env["tax.shelter.certificate"] tax_shelter = tax_shelter_mgr.sudo().browse(oid) try: if tax_shelter.partner_id != partner: @@ -101,19 +105,22 @@ class TaxShelterWebsiteAccount(WebsiteAccount): except MissingError: raise NotFound() # Get the pdf - report_mgr = request.env['report'] + report_mgr = request.env["report"] pdf = report_mgr.sudo().get_pdf( tax_shelter, - 'easy_my_coop_taxshelter_report.tax_shelter_subscription_report' + "easy_my_coop_taxshelter_report.tax_shelter_subscription_report", ) - filename = "Tax Shelter Certificate - %s - %s" % ( - partner.name, - tax_shelter.declaration_id.fiscal_year + filename = "Tax Shelter Certificate - {} - {}".format( + partner.name, tax_shelter.declaration_id.fiscal_year ) return self._render_pdf(pdf, filename) - @http.route(['/my/share_certificate/pdf/'], - type='http', auth="user", website=True) + @http.route( + ["/my/share_certificate/pdf/"], + type="http", + auth="user", + website=True, + ) def get_share_certificate_pdf(self, oid=-1): """Render the Share Certificate pdf of the given Tax Shelter Report @@ -121,7 +128,7 @@ class TaxShelterWebsiteAccount(WebsiteAccount): # Get the share certificate and raise an error if the user # is not allowed to access to it or if the object is not found. partner = request.env.user.partner_id - tax_shelter_mgr = request.env['tax.shelter.certificate'] + tax_shelter_mgr = request.env["tax.shelter.certificate"] tax_shelter = tax_shelter_mgr.sudo().browse(oid) try: if tax_shelter.partner_id != partner: @@ -131,22 +138,21 @@ class TaxShelterWebsiteAccount(WebsiteAccount): except MissingError: raise NotFound() # Get the pdf - report_mgr = request.env['report'] + report_mgr = request.env["report"] pdf = report_mgr.sudo().get_pdf( tax_shelter, - 'easy_my_coop_taxshelter_report.tax_shelter_shares_report' + "easy_my_coop_taxshelter_report.tax_shelter_shares_report", ) - filename = "Share Certificate - %s - %s" % ( - partner.name, - tax_shelter.declaration_id.fiscal_year + filename = "Share Certificate - {} - {}".format( + partner.name, tax_shelter.declaration_id.fiscal_year ) return self._render_pdf(pdf, filename) def _render_pdf(self, pdf, filename): """Render a http response for a pdf""" pdfhttpheaders = [ - ('Content-Disposition', 'inline; filename="%s.pdf"' % filename), - ('Content-Type', 'application/pdf'), - ('Content-Length', len(pdf)) + ("Content-Disposition", 'inline; filename="%s.pdf"' % filename), + ("Content-Type", "application/pdf"), + ("Content-Length", len(pdf)), ] return request.make_response(pdf, headers=pdfhttpheaders) diff --git a/partner_age/__manifest__.py b/partner_age/__manifest__.py index a0b0a5b..3f16dae 100644 --- a/partner_age/__manifest__.py +++ b/partner_age/__manifest__.py @@ -5,18 +5,12 @@ { "name": "Partner Age", "version": "12.0.1.0.0", - "depends": [ - "easy_my_coop", - "partner_contact_birthdate"], + "depends": ["easy_my_coop", "partner_contact_birthdate"], "author": "Houssine BAKKALI ", "category": "Cooperative management", - 'website': "www.coopiteasy.be", + "website": "www.coopiteasy.be", "license": "AGPL-3", - "description": """ - This module computes the age of the partner. - """, - 'data': [ - 'view/partner_view.xml', - ], - 'installable': True, + "summary": "This module computes the age of the partner.", + "data": ["view/partner_view.xml"], + "installable": True, } diff --git a/partner_age/models/partner.py b/partner_age/models/partner.py index 5d27b53..d7ea53a 100644 --- a/partner_age/models/partner.py +++ b/partner_age/models/partner.py @@ -1,34 +1,41 @@ -from datetime import datetime -from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DFORMAT - -from openerp import models, fields, api - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - def _search_age(self, operator, value): - if operator not in ('=', '!=', '<', '<=', '>', '>=', 'in', 'not in'): - return [] - query = """SELECT id - FROM "%s" - WHERE extract(year from age(CURRENT_DATE, - birthdate_date)) %s %%s""" % \ - (self._table, operator) - self.env.cr.execute(query, (value,)) - ids = [t[0] for t in self.env.cr.fetchall()] - return [('id', 'in', ids)] - - @api.one - @api.depends('birthdate_date') - def _compute_age(self): - if self.birthdate_date: - dBday = datetime.strptime(str(self.birthdate_date), - OE_DFORMAT).date() - dToday = datetime.now().date() - self.age = dToday.year - dBday.year - (( - dToday.month, dToday.day) < (dBday.month, dBday.day)) - - age = fields.Integer(string='Age', - compute='_compute_age', - search='_search_age') +from datetime import datetime + +from openerp import api, fields, models +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as OE_DFORMAT + + +class ResPartner(models.Model): + _inherit = "res.partner" + + def _search_age(self, operator, value): + if operator not in ("=", "!=", "<", "<=", ">", ">=", "in", "not in"): + return [] + query = """SELECT id + FROM "%s" + WHERE extract(year from age(CURRENT_DATE, + birthdate_date)) %s %%s""" % ( + self._table, + operator, + ) + self.env.cr.execute(query, (value,)) + ids = [t[0] for t in self.env.cr.fetchall()] + return [("id", "in", ids)] + + @api.multi + @api.depends("birthdate_date") + def _compute_age(self): + self.ensure_one() + if self.birthdate_date: + dBday = datetime.strptime( + str(self.birthdate_date), OE_DFORMAT + ).date() + dToday = datetime.now().date() + self.age = ( + dToday.year + - dBday.year + - ((dToday.month, dToday.day) < (dBday.month, dBday.day)) + ) + + age = fields.Integer( + string="Age", compute="_compute_age", search="_search_age" + ) diff --git a/theme_light/__manifest__.py b/theme_light/__manifest__.py index 5c5f3e1..d4c1e44 100644 --- a/theme_light/__manifest__.py +++ b/theme_light/__manifest__.py @@ -2,21 +2,15 @@ # - Houssine BAKKALI - # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). { - 'name': 'Theme light', - 'description': 'extract of the theme zen', - 'category': 'Website', - 'version': '12.0.1.0.0', - 'author': 'Benjamin Dugardin', - 'author': 'Houssine BAKKALI', - 'website': "www.coopiteasy.be", - 'depends': ['base', - 'web', - 'website_theme_install' - ], - 'data': [ - 'views/layout_template.xml', - 'report/header_report_G002.xml', - ], - 'installable': True, - 'application': True, + "name": "Theme light", + "summary": "extract of the theme zen", + "license": "AGPL-3", + "category": "Website", + "version": "12.0.1.0.0", + "author": "Benjamin Dugardin," "Houssine BAKKALI," "Coop IT Easy SCRLfs", + "website": "www.coopiteasy.be", + "depends": ["base", "web", "website_theme_install"], + "data": ["views/layout_template.xml", "report/header_report_G002.xml"], + "installable": True, + "application": True, } diff --git a/website_recaptcha_reloaded/__manifest__.py b/website_recaptcha_reloaded/__manifest__.py index a0f5ea9..73bc5c2 100644 --- a/website_recaptcha_reloaded/__manifest__.py +++ b/website_recaptcha_reloaded/__manifest__.py @@ -1,26 +1,14 @@ # Copyright 2004 Tech-Receptives Solutions Pvt. Ltd. # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). { - 'name': 'Website reCAPTCHA Reloaded', - 'version': '12.0.0.1', - 'category': 'Website', - 'depends': ['website'], - 'author': 'Tech Receptives', - 'license': 'AGPL-3', - 'website': 'https://www.techreceptives.com', - 'description': """ - Odoo Website reCAPTCHA Reloaded - ================================ - This modules allows you to integrate Google reCAPTCHA v2.0 to your website - forms. You can configure your Google reCAPTCHA site and public keys - in "Settings" -> "Website Settings" - - You will need to install various website__recaptcha modules - to use it in your various pages. - """, - 'data': [ - 'views/website_view.xml', - 'views/res_config.xml', - ], - 'installable': True, + "name": "Website reCAPTCHA Reloaded", + "version": "12.0.0.0.1", + "category": "Website", + "depends": ["website"], + "author": "Tech Receptives", + "license": "AGPL-3", + "website": "https://www.techreceptives.com", + "summary": "Add google recaptcha to forms.", + "data": ["views/website_view.xml", "views/res_config.xml"], + "installable": True, } diff --git a/website_recaptcha_reloaded/models/res_config.py b/website_recaptcha_reloaded/models/res_config.py index 4f38a4f..d2debfb 100644 --- a/website_recaptcha_reloaded/models/res_config.py +++ b/website_recaptcha_reloaded/models/res_config.py @@ -4,25 +4,23 @@ from odoo import api, fields, models -class website_config_settings(models.TransientModel): - _inherit = 'res.config.settings' +class WebsiteConfigSettings(models.TransientModel): + _inherit = "res.config.settings" recaptcha_key_site = fields.Char( - related='website_id.recaptcha_key_site', - readonly=False, + related="website_id.recaptcha_key_site", readonly=False ) recaptcha_key_secret = fields.Char( - related='website_id.recaptcha_key_secret', - readonly=False, + related="website_id.recaptcha_key_secret", readonly=False ) has_google_recaptcha = fields.Boolean( - 'Google reCaptcha', - compute='_compute_has_google_recaptcha', - inverse='_inverse_has_google_recaptcha', + "Google reCaptcha", + compute="_compute_has_google_recaptcha", + inverse="_inverse_has_google_recaptcha", readonly=False, ) - @api.depends('website_id') + @api.depends("website_id") def _compute_has_google_recaptcha(self): self.has_google_recaptcha = bool(self.recaptcha_key_site) diff --git a/website_recaptcha_reloaded/website.py b/website_recaptcha_reloaded/website.py index 8ba7217..b39cfe6 100644 --- a/website_recaptcha_reloaded/website.py +++ b/website_recaptcha_reloaded/website.py @@ -1,15 +1,16 @@ # Copyright 2019 Simone Orsi - Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError import requests -URL = 'https://www.google.com/recaptcha/api/siteverify' +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + +URL = "https://www.google.com/recaptcha/api/siteverify" class Website(models.Model): - _inherit = 'website' + _inherit = "website" recaptcha_key_site = fields.Char() recaptcha_key_secret = fields.Char() @@ -17,27 +18,31 @@ class Website(models.Model): @api.model def _get_error_message(self, errorcode=None): mapping = { - 'missing-input-secret': _('The secret parameter is missing.'), - 'invalid-input-secret': - _('The secret parameter is invalid or malformed.'), - 'missing-input-response': _('The response parameter is missing.'), - 'invalid-input-response': - _('The response parameter is invalid or malformed.'), + "missing-input-secret": _("The secret parameter is missing."), + "invalid-input-secret": _( + "The secret parameter is invalid or malformed." + ), + "missing-input-response": _("The response parameter is missing."), + "invalid-input-response": _( + "The response parameter is invalid or malformed." + ), } - return mapping.get(errorcode, _('There was a problem with ' - 'the captcha entry.')) + return mapping.get( + errorcode, _("There was a problem with " "the captcha entry.") + ) def is_captcha_valid(self, response): - get_res = {'secret': self.recaptcha_key_secret, - 'response': response} + get_res = {"secret": self.recaptcha_key_secret, "response": response} res = requests.post(URL, data=get_res).json() - error_msg = "\n".join(self._get_error_message(error) - for error in res.get('error-codes', [])) + error_msg = "\n".join( + self._get_error_message(error) + for error in res.get("error-codes", []) + ) if error_msg: raise ValidationError(error_msg) - if not res.get('success'): + if not res.get("success"): raise ValidationError(self._get_error_message()) return True