From 29d88ac7be4a3207966e99e57e80bef0f59b2a3c Mon Sep 17 00:00:00 2001 From: Franco Leyes Date: Mon, 20 Jan 2025 14:20:01 -0300 Subject: [PATCH] [IMP] *: automatic changes by pre-commit --- .pre-commit-config.yaml | 2 +- academic/__manifest__.py | 129 ++++++----- academic/hooks.py | 7 +- academic/models/academic_division.py | 6 +- academic/models/academic_group.py | 166 +++++++------- academic/models/academic_level.py | 28 ++- academic/models/academic_promotion.py | 7 +- academic/models/academic_section.py | 7 +- academic/models/academic_study_plan.py | 14 +- academic/models/academic_subject.py | 17 +- academic/models/account_move.py | 47 ++-- academic/models/account_move_line.py | 10 +- academic/models/hr.py | 61 ++--- academic/models/res_company.py | 16 +- academic/models/res_partner.py | 217 ++++++++++-------- academic/models/res_partner_category.py | 6 +- academic/models/res_partner_family.py | 13 +- academic/models/res_partner_link.py | 29 +-- academic/models/res_partner_relationship.py | 7 +- academic/models/res_partner_role.py | 11 +- academic/models/res_users.py | 14 +- academic/models/sale_order.py | 39 ++-- academic/wizards/account_move_send.py | 4 +- academic/wizards/portal_wizard.py | 44 ++-- academic/wizards/portal_wizard_user.py | 59 +++-- academic_account_interests/__manifest__.py | 29 +-- .../models/res_company_interest.py | 89 +++---- academic_partner_code/__manifest__.py | 29 +-- academic_partner_code/models/res_partner.py | 17 +- academic_sale_subscription/README.rst | 2 +- academic_sale_subscription/__manifest__.py | 39 ++-- .../models/res_partner.py | 19 +- .../models/sale_order.py | 35 ++- .../models/sale_order_line.py | 16 +- .../models/sale_subscription_plan.py | 12 +- .../wizard/academic_order_wizard.py | 175 ++++++++------ academic_split_name/__manifest__.py | 28 +-- academic_split_name/models/res_partner.py | 6 +- 38 files changed, 753 insertions(+), 703 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2e0cca51..0b82f519 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ exclude: | (?x) - + # We don't want to mess with tool-generated files .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| # Library files can have extraneous formatting (even minimized) diff --git a/academic/__manifest__.py b/academic/__manifest__.py index dfc5d6e9..b2917500 100644 --- a/academic/__manifest__.py +++ b/academic/__manifest__.py @@ -18,72 +18,71 @@ # ############################################################################## { - 'name': 'Academic', - 'version': "18.0.1.2.0", - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'images': [ + "name": "Academic", + "version": "18.0.1.2.0", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "images": [], + "depends": [ + "portal_backend", + "board", + "hr", + "website", + "board", + "sale_management", + "account", + "contacts", + "report_aeroo", ], - 'depends': [ - 'portal_backend', - 'board', - 'hr', - 'website', - 'board', - 'sale_management', - 'account', - 'contacts', - 'report_aeroo', + "data": [ + "security/academic_security.xml", + "security/ir.model.access.csv", + "data/res_partner_role_data.xml", + "data/res_users_data.xml", + "data/ir_cron.xml", + "security/res_partner_category.xml", + "views/academic_menuitem.xml", + "views/res_partner_views.xml", + "views/academic_group_views.xml", + "views/academic_division_views.xml", + "views/academic_level_views.xml", + "views/academic_study_plan_views.xml", + "views/academic_promotion_views.xml", + "views/academic_section_views.xml", + "views/academic_subject_views.xml", + "views/hr_views.xml", + "views/res_users_views.xml", + "views/res_company_views.xml", + "views/login_page.xml", + "views/sale_order_views.xml", + "views/res_partner_link_views.xml", + "views/res_partner_relationship_views.xml", + "views/account_move_views.xml", + "wizards/portal_wizard_views.xml", + "report/ir_actions_report.xml", + "views/res_partner_category.xml", + "report/report_invoice.xml", ], - 'data': [ - 'security/academic_security.xml', - 'security/ir.model.access.csv', - 'data/res_partner_role_data.xml', - 'data/res_users_data.xml', - 'data/ir_cron.xml', - 'security/res_partner_category.xml', - 'views/academic_menuitem.xml', - 'views/res_partner_views.xml', - 'views/academic_group_views.xml', - 'views/academic_division_views.xml', - 'views/academic_level_views.xml', - 'views/academic_study_plan_views.xml', - 'views/academic_promotion_views.xml', - 'views/academic_section_views.xml', - 'views/academic_subject_views.xml', - 'views/hr_views.xml', - 'views/res_users_views.xml', - 'views/res_company_views.xml', - 'views/login_page.xml', - 'views/sale_order_views.xml', - 'views/res_partner_link_views.xml', - 'views/res_partner_relationship_views.xml', - 'views/account_move_views.xml', - 'wizards/portal_wizard_views.xml', - 'report/ir_actions_report.xml', - 'views/res_partner_category.xml', - 'report/report_invoice.xml', + "demo": [ + "demo/res_partner_relationship_demo.xml", + "demo/res_partner_demo.xml", + "demo/academic.subject.csv", + "demo/academic.section.csv", + "demo/academic.level.csv", + "demo/academic.promotion.csv", + "demo/res.partner.csv", + "demo/res.partner.link.csv", + "demo/academic.division.csv", + "demo/academic.study.plan.csv", + "demo/res_company_demo.xml", + "demo/academic_group.xml", + "demo/res_users_demo.xml", ], - 'demo': [ - 'demo/res_partner_relationship_demo.xml', - 'demo/res_partner_demo.xml', - 'demo/academic.subject.csv', - 'demo/academic.section.csv', - 'demo/academic.level.csv', - 'demo/academic.promotion.csv', - 'demo/res.partner.csv', - 'demo/res.partner.link.csv', - 'demo/academic.division.csv', - 'demo/academic.study.plan.csv', - 'demo/res_company_demo.xml', - 'demo/academic_group.xml', - 'demo/res_users_demo.xml', - ], - 'installable': True, - 'auto_install': False, - 'application': False, - 'post_init_hook': 'post_init_hook', + "installable": True, + "auto_install": False, + "application": False, + "post_init_hook": "post_init_hook", } diff --git a/academic/hooks.py b/academic/hooks.py index 0554d014..2f53b93a 100644 --- a/academic/hooks.py +++ b/academic/hooks.py @@ -3,12 +3,13 @@ # directory ############################################################################## + def post_init_hook(env): - ''' Esto lo dejamos solo para los templates de órdenes de venta y no en facturas debido a que + """Esto lo dejamos solo para los templates de órdenes de venta y no en facturas debido a que en facturas hay un wizard custom y no heredamos el método de _message_get_default_recipients, si se mandan facturas desde el chatter no se van a sugerir, aunque en facturas primero se confirma (al confirmar se agregan los responsables de pago como seguidores) y luego se enviaría la factura, así que siempre van a enviarse a los seguidores de la factura. - ''' - templates = env['mail.template'].search([('model_id.model', '=', 'sale.order')]) + """ + templates = env["mail.template"].search([("model_id.model", "=", "sale.order")]) templates.use_default_to = True diff --git a/academic/models/academic_division.py b/academic/models/academic_division.py index a28cfdbd..85f1bb74 100644 --- a/academic/models/academic_division.py +++ b/academic/models/academic_division.py @@ -2,12 +2,12 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class AcademicDivision(models.Model): - _name = 'academic.division' - _description = 'division' + _name = "academic.division" + _description = "division" name = fields.Char( required=True, diff --git a/academic/models/academic_group.py b/academic/models/academic_group.py index ba583985..f82741a9 100644 --- a/academic/models/academic_group.py +++ b/academic/models/academic_group.py @@ -2,130 +2,117 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, models, fields -from datetime import date import random import string +from datetime import date + +from odoo import api, fields, models class AcademicGroup(models.Model): - _name = 'academic.group' - _description = 'group' - _order = 'name' + _name = "academic.group" + _description = "group" + _order = "name" _sql_constraints = [ - ('group_unique', - 'unique(subject_id, company_id, level_id, year, division_id)', - 'Group should be unique per Institution, Subject,' - ' Course-Division and Year')] + ( + "group_unique", + "unique(subject_id, company_id, level_id, year, division_id)", + "Group should be unique per Institution, Subject," " Course-Division and Year", + ) + ] - type = fields.Selection([ - ('student', 'Student'), - ('teacher', 'Teacher'), - ('administrator', 'Administrator'), - ('gral_administrator', 'gral_administrator'), - ('parent', 'Relative')] - ) - year = fields.Integer( - required=True, - default=date.today().year, - index=True + type = fields.Selection( + [ + ("student", "Student"), + ("teacher", "Teacher"), + ("administrator", "Administrator"), + ("gral_administrator", "gral_administrator"), + ("parent", "Relative"), + ] ) + year = fields.Integer(required=True, default=date.today().year, index=True) division_id = fields.Many2one( - 'academic.division', - string='Division', + "academic.division", + string="Division", ) company_id = fields.Many2one( - 'res.company', - string='Company', + "res.company", + string="Company", required=True, - context={'default_is_company': True}, - default=lambda self: self.env.company + context={"default_is_company": True}, + default=lambda self: self.env.company, ) - study_plan_level_ids = fields.Many2many(related='company_id.study_plan_id.level_ids') + study_plan_level_ids = fields.Many2many(related="company_id.study_plan_id.level_ids") level_id = fields.Many2one( - 'academic.level', - string='Level', + "academic.level", + string="Level", required=True, ) - subject_id = fields.Many2one( - 'academic.subject', - string='Subject', - required=False, - index=True - ) + subject_id = fields.Many2one("academic.subject", string="Subject", required=False, index=True) teacher_id = fields.Many2one( - 'res.partner', - string='Teacher', + "res.partner", + string="Teacher", required=False, - context={'default_partner_type': 'teacher'}, - domain=[('partner_type', '=', 'teacher')], + context={"default_partner_type": "teacher"}, + domain=[("partner_type", "=", "teacher")], ) student_ids = fields.Many2many( - 'res.partner', - 'academic_student_group_ids_student_ids_rel', - 'group_id', - 'partner_id', - string='Student', - context={'default_partner_type': 'student'}, - domain=[('partner_type', '=', 'student')], - ) - name = fields.Char( - compute='_compute_name', - store=True + "res.partner", + "academic_student_group_ids_student_ids_rel", + "group_id", + "partner_id", + string="Student", + context={"default_partner_type": "student"}, + domain=[("partner_type", "=", "student")], ) + name = fields.Char(compute="_compute_name", store=True) active = fields.Boolean(default=True) student_ids_count = fields.Integer( - string='Student Count', - compute='_compute_student_ids_count', + string="Student Count", + compute="_compute_student_ids_count", ) - @api.depends( - 'subject_id', - 'company_id', - 'level_id', - 'division_id', - 'year') + @api.depends("subject_id", "company_id", "level_id", "division_id", "year") def _compute_name(self): - """ Forms complete name of location from parent location to + """Forms complete name of location from parent location to child location. @return: Dictionary of values """ for line in self: name = line.company_id.name - name += ', ' + line.level_id.name + name += ", " + line.level_id.name if line.division_id: - name += ' ' + line.division_id.name - name += ' - ' + line.level_id.section_id.name - name += ' - ' + self.env._('Year: ') + str(line.year) + name += " " + line.division_id.name + name += " - " + line.level_id.section_id.name + name += " - " + self.env._("Year: ") + str(line.year) line.name = name def create_students_users(self): - ''' + """ This function create users if they don't exist for students related to this group. - ''' + """ self.student_ids.quickly_create_portal_user() # Creamos contrasenas para todos los students que no tengan una # explicita (no hashed) - for user in \ - self.student_ids.mapped('user_ids')\ - .filtered(lambda x: not x.password): - user.password = ''.join(random.choice( - string.ascii_uppercase + string.digits) for _ in range(6)) + for user in self.student_ids.mapped("user_ids").filtered(lambda x: not x.password): + user.password = "".join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6)) def print_users(self): - ''' + """ This function prints a report with users login and password. - ''' + """ self.ensure_one() self.create_students_users() - report = self.env['ir.actions.report'].search( - [('report_name', '=', 'academic.template_report_users')], - limit=1).report_action(self) + report = ( + self.env["ir.actions.report"] + .search([("report_name", "=", "academic.template_report_users")], limit=1) + .report_action(self) + ) return report - @api.depends('student_ids') + @api.depends("student_ids") def _compute_student_ids_count(self): for group in self: group.student_ids_count = len(group.student_ids) @@ -134,17 +121,22 @@ def create_next_year_groups(self): # estamos pasando de un año a otro sin usar study plan por lo siguiente: # a) hay muchos colegios que no lo tienen bien implmentado # b) los study plan no pueden reflejar todos los casos todavia (por ) - + for rec in self: - next_group = rec.env['academic.group'].search([ - ('year', '=', rec.year + 1), - ('company_id', '=', rec.company_id.id), - ('level_id', '=', rec.level_id.id), - ('division_id', '=', rec.division_id.id) - ], limit=1) + next_group = rec.env["academic.group"].search( + [ + ("year", "=", rec.year + 1), + ("company_id", "=", rec.company_id.id), + ("level_id", "=", rec.level_id.id), + ("division_id", "=", rec.division_id.id), + ], + limit=1, + ) if not next_group: - next_group = rec.copy(default={ - 'year': rec.year + 1, - 'student_ids': False, - }) + next_group = rec.copy( + default={ + "year": rec.year + 1, + "student_ids": False, + } + ) diff --git a/academic/models/academic_level.py b/academic/models/academic_level.py index 04a4ad62..1279ecb1 100644 --- a/academic/models/academic_level.py +++ b/academic/models/academic_level.py @@ -2,34 +2,32 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, models, fields +from odoo import api, fields, models class AcademicLevel(models.Model): + _name = "academic.level" + _description = "level" + _order = "sequence" + _rec_names_search = ["name", "section_id.name"] - _name = 'academic.level' - _description = 'level' - _order = 'sequence' - _rec_names_search = ['name', 'section_id.name'] - - sequence = fields.Integer( - ) + sequence = fields.Integer() name = fields.Char( required=True, translate=True, ) section_id = fields.Many2one( - 'academic.section', - string='Section', + "academic.section", + string="Section", required=True, ) group_ids = fields.One2many( - 'academic.group', - 'level_id', - string='Groups', + "academic.group", + "level_id", + string="Groups", ) - @api.depends('name', 'section_id.name') + @api.depends("name", "section_id.name") def _compute_display_name(self): for rec in self: - rec.display_name = rec.name + ' - ' + rec.section_id.name if rec.name else '' + rec.display_name = rec.name + " - " + rec.section_id.name if rec.name else "" diff --git a/academic/models/academic_promotion.py b/academic/models/academic_promotion.py index fd97dff4..5dbe1ab3 100644 --- a/academic/models/academic_promotion.py +++ b/academic/models/academic_promotion.py @@ -2,13 +2,12 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class AcademicPromotion(models.Model): - - _name = 'academic.promotion' - _description = 'promotion' + _name = "academic.promotion" + _description = "promotion" name = fields.Char( required=True, diff --git a/academic/models/academic_section.py b/academic/models/academic_section.py index db989860..fd71fa8d 100644 --- a/academic/models/academic_section.py +++ b/academic/models/academic_section.py @@ -2,13 +2,12 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class AcademicSection(models.Model): - - _name = 'academic.section' - _description = 'section' + _name = "academic.section" + _description = "section" name = fields.Char( required=True, diff --git a/academic/models/academic_study_plan.py b/academic/models/academic_study_plan.py index 400d9fab..f6fd3463 100644 --- a/academic/models/academic_study_plan.py +++ b/academic/models/academic_study_plan.py @@ -2,15 +2,15 @@ class StudyPlan(models.Model): - _name = 'academic.study.plan' - _description = 'Study Plan' + _name = "academic.study.plan" + _description = "Study Plan" name = fields.Char(required=True) level_ids = fields.Many2many( - 'academic.level', - 'academic_study_plan_ids_level_ids_rel', - 'academic_study_plan_id', - 'level_id', - string='Levels', + "academic.level", + "academic_study_plan_ids_level_ids_rel", + "academic_study_plan_id", + "level_id", + string="Levels", ) diff --git a/academic/models/academic_subject.py b/academic/models/academic_subject.py index e9f67051..1f81653b 100644 --- a/academic/models/academic_subject.py +++ b/academic/models/academic_subject.py @@ -2,22 +2,19 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class AcademicSubject(models.Model): - - _name = 'academic.subject' - _description = 'subject' + _name = "academic.subject" + _description = "subject" name = fields.Char( required=True, ) group_ids = fields.One2many( - 'academic.group', - 'subject_id', - string='Groups', + "academic.group", + "subject_id", + string="Groups", ) - employees_asignatures_ids = fields.One2many( - comodel_name='hr.employee.asignatures', - inverse_name='subject_id') + employees_asignatures_ids = fields.One2many(comodel_name="hr.employee.asignatures", inverse_name="subject_id") diff --git a/academic/models/account_move.py b/academic/models/account_move.py index c769ce07..2bc357f0 100644 --- a/academic/models/account_move.py +++ b/academic/models/account_move.py @@ -1,40 +1,55 @@ -from odoo import models, fields, api +from odoo import api, fields, models from odoo.exceptions import ValidationError class AccountMove(models.Model): - _inherit = 'account.move' + _inherit = "account.move" # Este campo solo lo uso para calcular el dominio del student_id ya que implica una búsqueda por el rol de pago. - student_ids = fields.Many2many('res.partner', string="Students List", compute="_compute_student_ids") - student_id = fields.Many2one('res.partner', domain="[('id', 'in', student_ids), ('partner_type', '=', 'student')]", index=True) + student_ids = fields.Many2many("res.partner", string="Students List", compute="_compute_student_ids") + student_id = fields.Many2one( + "res.partner", domain="[('id', 'in', student_ids), ('partner_type', '=', 'student')]", index=True + ) - @api.constrains('student_id', 'move_type') + @api.constrains("student_id", "move_type") def _check_student(self): invoices_wo_student = self.filtered(lambda x: x.move_type in ["out_invoice", "out_refund"] and not x.student_id) if invoices_wo_student: msg = self.env._("Las facturas de clientes y notas de debito debe tener asociado siempre un alumno.") if len(invoices_wo_student) > 1: - msg += ".\n" + self.env._("Los siguientes documentos no cumplen esa condición:") + "\n\n - %s" % '\n - '.join(invoices_wo_student.mapped('display_name')) + msg += ( + ".\n" + + self.env._("Los siguientes documentos no cumplen esa condición:") + + "\n\n - %s" % "\n - ".join(invoices_wo_student.mapped("display_name")) + ) raise ValidationError(msg) - @api.depends('partner_id') + @api.depends("partner_id") def _compute_student_ids(self): for rec in self: if rec.partner_id: - student_ids = self.env['res.partner.link'].search( - [('partner_id', '=', rec.partner_id.id), ('role_ids', 'in', self.env.ref('academic.paying_role').id)] - ).mapped('student_id.id') + student_ids = ( + self.env["res.partner.link"] + .search( + [ + ("partner_id", "=", rec.partner_id.id), + ("role_ids", "in", self.env.ref("academic.paying_role").id), + ] + ) + .mapped("student_id.id") + ) rec.student_ids = [(6, 0, student_ids)] else: rec.student_ids = [(5, 0, 0)] def _post(self, soft=True): for rec in self: - partners_invoice = rec.student_id.get_payment_responsible() if rec.student_id else self.env['res.partner'] - rec.message_subscribe([ - payment_responsible.id - for payment_responsible in rec.partner_id | partners_invoice - if payment_responsible not in rec.sudo().message_partner_ids - ]) + partners_invoice = rec.student_id.get_payment_responsible() if rec.student_id else self.env["res.partner"] + rec.message_subscribe( + [ + payment_responsible.id + for payment_responsible in rec.partner_id | partners_invoice + if payment_responsible not in rec.sudo().message_partner_ids + ] + ) return super()._post(soft=soft) diff --git a/academic/models/account_move_line.py b/academic/models/account_move_line.py index fd301a33..3423487e 100644 --- a/academic/models/account_move_line.py +++ b/academic/models/account_move_line.py @@ -1,13 +1,13 @@ -from odoo import models, fields, api +from odoo import api, fields, models class AccountMoveLine(models.Model): - _inherit = 'account.move.line' + _inherit = "account.move.line" - student_id = fields.Many2one('res.partner', related='move_id.student_id', readonly=True) + student_id = fields.Many2one("res.partner", related="move_id.student_id", readonly=True) ref = fields.Char(related=False, compute="_compute_ref") - @api.depends('move_id.ref', 'move_id.student_id', 'move_id.student_id.name') + @api.depends("move_id.ref", "move_id.student_id", "move_id.student_id.name") def _compute_ref(self): for rec in self: - rec.ref = ' - '.join(filter(None, [rec.move_id.ref, rec.move_id.student_id.name])) + rec.ref = " - ".join(filter(None, [rec.move_id.ref, rec.move_id.student_id.name])) diff --git a/academic/models/hr.py b/academic/models/hr.py index 0295017b..a69d2a4a 100644 --- a/academic/models/hr.py +++ b/academic/models/hr.py @@ -3,52 +3,53 @@ class EmployeeAsignatures(models.Model): - _name = 'hr.employee.asignatures' - _description = 'Employee Asignatures' - _rec_name = 'teacher_id' + _name = "hr.employee.asignatures" + _description = "Employee Asignatures" + _rec_name = "teacher_id" - name = fields.Char(compute='_compute_name') + name = fields.Char(compute="_compute_name") - teacher_id = fields.Many2one( - 'hr.employee', - required=True, - ondelete='cascade' - ) + teacher_id = fields.Many2one("hr.employee", required=True, ondelete="cascade") subject_id = fields.Many2one( - comodel_name='academic.subject', + comodel_name="academic.subject", ) level_ids = fields.Many2many( - 'academic.level', - 'hr_employee_asignatures_ids_level_ids_rel', - 'hr_employee_asignature_id', - 'level_id', + "academic.level", + "hr_employee_asignatures_ids_level_ids_rel", + "hr_employee_asignature_id", + "level_id", ) - @api.constrains('teacher_id', 'subject_id') + @api.constrains("teacher_id", "subject_id") def _check_repeated_subject_teacher(self): for record in self: - if self.env['hr.employee.asignatures'].search_count([ - ('id', '!=', record.id), - ('subject_id', '=', record.subject_id.id), - ('teacher_id', '=', record.teacher_id.id)]): - raise ValidationError(self.env._( - 'There is another record that contains the same subject \ - and teacher')) - - @api.constrains('subject_id') + if self.env["hr.employee.asignatures"].search_count( + [ + ("id", "!=", record.id), + ("subject_id", "=", record.subject_id.id), + ("teacher_id", "=", record.teacher_id.id), + ] + ): + raise ValidationError( + self.env._( + "There is another record that contains the same subject \ + and teacher" + ) + ) + + @api.constrains("subject_id") def _check_level_ids(self): for record in self: if not record.level_ids: - raise ValidationError(self.env._( - 'You have to add at least a level for that subject')) + raise ValidationError(self.env._("You have to add at least a level for that subject")) class Employee(models.Model): - _inherit = 'hr.employee' + _inherit = "hr.employee" asignatures_ids = fields.One2many( - comodel_name='hr.employee.asignatures', - inverse_name='teacher_id', + comodel_name="hr.employee.asignatures", + inverse_name="teacher_id", ) - study_plan_level_ids = fields.Many2many(related='company_id.study_plan_id.level_ids') + study_plan_level_ids = fields.Many2many(related="company_id.study_plan_id.level_ids") diff --git a/academic/models/res_company.py b/academic/models/res_company.py index 1f7d2428..a5f2aedc 100644 --- a/academic/models/res_company.py +++ b/academic/models/res_company.py @@ -2,20 +2,16 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResCompany(models.Model): - - _inherit = 'res.company' + _inherit = "res.company" company_group_ids = fields.One2many( - 'academic.group', - 'company_id', - string='Groups', - ) - study_plan_id = fields.Many2one( - comodel_name='academic.study.plan', - string='Plan de Estudio' + "academic.group", + "company_id", + string="Groups", ) + study_plan_id = fields.Many2one(comodel_name="academic.study.plan", string="Plan de Estudio") family_required = fields.Boolean() diff --git a/academic/models/res_partner.py b/academic/models/res_partner.py index af2ef61d..397c60c6 100644 --- a/academic/models/res_partner.py +++ b/academic/models/res_partner.py @@ -2,58 +2,57 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, models, fields, _ -from odoo.exceptions import UserError, ValidationError from datetime import date +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError + class ResPartner(models.Model): - - _inherit = 'res.partner' + _inherit = "res.partner" _check_company_auto = True _check_company_domain = models.check_company_domain_parent_of parent_id = fields.Many2one(check_company=True) - partner_type = fields.Selection([ - ('student', 'Student'), - ('teacher', 'Teacher'), - ('administrator', 'Administrator'), - ('gral_administrator', 'General Administrator'), - ('parent', 'Relative'), - ('other', 'Other'), - ('family', 'Family'), - ], + partner_type = fields.Selection( + [ + ("student", "Student"), + ("teacher", "Teacher"), + ("administrator", "Administrator"), + ("gral_administrator", "General Administrator"), + ("parent", "Relative"), + ("other", "Other"), + ("family", "Family"), + ], change_default=True, - string='Tipo de Contacto Académico', - compute='_compute_partner_type', + string="Tipo de Contacto Académico", + compute="_compute_partner_type", readonly=False, store=True, ) section_id = fields.Many2one( - 'academic.section', - string='Section', + "academic.section", + string="Section", ) promotion_id = fields.Many2one( - 'academic.promotion', - string='Promotion', + "academic.promotion", + string="Promotion", ) teacher_group_ids = fields.One2many( - 'academic.group', - 'teacher_id', - string='Teacher Groups', + "academic.group", + "teacher_id", + string="Teacher Groups", ) student_group_ids = fields.Many2many( - 'academic.group', - 'academic_student_group_ids_student_ids_rel', - 'partner_id', - 'group_id', - string='Student Groups', - ) - disabled_person = fields.Boolean( - help='¿Alumno/a con Dificultades de aprendizaje?' + "academic.group", + "academic_student_group_ids_student_ids_rel", + "partner_id", + "group_id", + string="Student Groups", ) + disabled_person = fields.Boolean(help="¿Alumno/a con Dificultades de aprendizaje?") sex = fields.Selection( - [('M', 'Male'), ('F', 'Female')], + [("M", "Male"), ("F", "Female")], ) file_number = fields.Char( copy=False, @@ -61,65 +60,84 @@ class ResPartner(models.Model): birthdate = fields.Date( copy=False, ) - admission_date = fields.Date( - ) - exit_date = fields.Date( - ) + admission_date = fields.Date() + exit_date = fields.Date() medical_insurance = fields.Char( copy=False, ) dni = fields.Char( - 'DNI', + "DNI", ) related_user_id = fields.Many2one( - 'res.users', - compute='_compute_related_user_id', + "res.users", + compute="_compute_related_user_id", ) student_link_ids = fields.One2many( - 'res.partner.link', 'student_id', string='Contactos y Roles', copy=True, - compute='_compute_student_links', readonly=False, store=True, recursive=True) + "res.partner.link", + "student_id", + string="Contactos y Roles", + copy=True, + compute="_compute_student_links", + readonly=False, + store=True, + recursive=True, + ) - @api.depends('parent_links_by_student', 'parent_id.student_link_ids') + @api.depends("parent_links_by_student", "parent_id.student_link_ids") def _compute_student_links(self): - """ Si se confirugan los contactos en la flia, los propagamos a los hijos. Lo hacemos así para que: + """Si se confirugan los contactos en la flia, los propagamos a los hijos. Lo hacemos así para que: a) en todo el codigo solo miremos siempre hijos, lo de la familia es un asistente - b) si se marca llevar en estudiantes ya van a tener toda la data que tenía la familia """ - for rec in self.filtered(lambda x: x.partner_type == 'student' and x.parent_id and not x.parent_links_by_student): + b) si se marca llevar en estudiantes ya van a tener toda la data que tenía la familia""" + for rec in self.filtered( + lambda x: x.partner_type == "student" and x.parent_id and not x.parent_links_by_student + ): commands = [] for link in rec.parent_id.student_link_ids: existing_link = rec.student_link_ids.filtered(lambda l: l.partner_id == link.partner_id) if existing_link: - existing_link.write({ - 'relationship_id': link.relationship_id.id, - 'note': link.note, - 'role_ids': [(6, 0, link.role_ids.ids)], - }) + existing_link.write( + { + "relationship_id": link.relationship_id.id, + "note": link.note, + "role_ids": [(6, 0, link.role_ids.ids)], + } + ) else: - commands.append((0, 0, { - 'partner_id': link.partner_id.id, - 'relationship_id': link.relationship_id.id, - 'note': link.note, - 'role_ids': [(6, 0, link.role_ids.ids)], - })) - parent_partner_ids = rec.parent_id.student_link_ids.mapped('partner_id') + commands.append( + ( + 0, + 0, + { + "partner_id": link.partner_id.id, + "relationship_id": link.relationship_id.id, + "note": link.note, + "role_ids": [(6, 0, link.role_ids.ids)], + }, + ) + ) + parent_partner_ids = rec.parent_id.student_link_ids.mapped("partner_id") obsolete_links = rec.student_link_ids.filtered(lambda l: l.partner_id not in parent_partner_ids) commands += [(2, link.id) for link in obsolete_links] if commands: rec.student_link_ids = commands - partner_link_ids = fields.One2many('res.partner.link', 'partner_id', string='Roles', copy=True) - links_by_student = fields.Boolean(string='Contactos y Roles por Estudiante') - company_family_required = fields.Boolean(related='company_id.family_required') - parent_links_by_student = fields.Boolean(related='parent_id.links_by_student', string="La familia define Contactos y Roles por Estudiante") + partner_link_ids = fields.One2many("res.partner.link", "partner_id", string="Roles", copy=True) + links_by_student = fields.Boolean(string="Contactos y Roles por Estudiante") + company_family_required = fields.Boolean(related="company_id.family_required") + parent_links_by_student = fields.Boolean( + related="parent_id.links_by_student", string="La familia define Contactos y Roles por Estudiante" + ) # creamos nuevo campo porque el child_ids como ya esta en la vista nos propaga el mode kanban # al hacerlo con mode tree nos simplfica bastante la herencia de vista porque no tenemos que agregar en el quick # create tantas cosas - student_ids = fields.One2many('res.partner', 'parent_id') - company_id = fields.Many2one(compute='_compute_company_id', store=True, readonly=False) + student_ids = fields.One2many("res.partner", "parent_id") + company_id = fields.Many2one(compute="_compute_company_id", store=True, readonly=False) # company_type = fields.Selection(selection_add=[('family', 'Family')]) # is_family = fields.Boolean() - same_dni_partner_id = fields.Many2one('res.partner', string='Partner with same DNI', compute='_compute_same_dni_partner_id', store=False) - current_main_group_id = fields.Many2one('academic.group', compute='_compute_current_main_group', store=True) + same_dni_partner_id = fields.Many2one( + "res.partner", string="Partner with same DNI", compute="_compute_same_dni_partner_id", store=False + ) + current_main_group_id = fields.Many2one("academic.group", compute="_compute_current_main_group", store=True) category_id = fields.Many2many(check_company=True) # @api.depends('is_family') @@ -134,33 +152,37 @@ def _compute_student_links(self): # families.is_family = True # return super(ResPartner, self - families)._write_company_type() - @api.constrains('company_id', 'partner_type', 'parent_id') + @api.constrains("company_id", "partner_type", "parent_id") def _check_family_configured(self): - if self.filtered(lambda x: x.partner_type == 'student' and x.company_id.family_required and x.parent_id.partner_type != 'family'): - raise UserError('En la institucion, los estudiantes deben estar vinculados a una familia') - - @api.constrains('parent_id', 'partner_type') + if self.filtered( + lambda x: x.partner_type == "student" + and x.company_id.family_required + and x.parent_id.partner_type != "family" + ): + raise UserError("En la institucion, los estudiantes deben estar vinculados a una familia") + + @api.constrains("parent_id", "partner_type") def _check_family_student_relation(self): - if self.filtered(lambda x: x.partner_type != 'student' and x.parent_id.partner_type == 'family'): - raise UserError('Los contactos de una familia solo pueden ser estudiantes') + if self.filtered(lambda x: x.partner_type != "student" and x.parent_id.partner_type == "family"): + raise UserError("Los contactos de una familia solo pueden ser estudiantes") def _compute_related_user_id(self): for rec in self: rec.related_user_id = rec.user_ids and rec.user_ids[0] - @api.depends('is_company') + @api.depends("is_company") def _compute_partner_type(self): self.filtered(lambda x: x.is_company and x.partner_type).partner_type = False def quickly_create_portal_user(self): - """ Metodo que crea o activa usuario inactivo en el grupo portal que + """Metodo que crea o activa usuario inactivo en el grupo portal que se defina """ # TODO: el metodo onchange_portal_id no existe. # Esto dejo de usarse pero queda el codigo por posible implementacion a futuro raise UserError(_("Esta función se encuentra en desarrollo!")) - @api.depends('parent_id') + @api.depends("parent_id") def _compute_company_id(self): """ Si soy parte de una compañía (o familia, es campo "parent_id"), queremos que todos los childs tengan misma company @@ -175,47 +197,40 @@ def _onchange_company_id(self): # anulamos el onchange nativo de odoo porque ahora lo hicimos compute return - @api.depends('dni') + @api.depends("dni") def _compute_same_dni_partner_id(self): - filtered_partners = self.filtered('dni') + filtered_partners = self.filtered("dni") for partner in filtered_partners: partner_id = partner._origin.id Partner = self.with_context(active_test=False).sudo() domain = [ - ('dni', '=', partner.dni), + ("dni", "=", partner.dni), ] if partner_id: - domain += [('id', '!=', partner_id)] + domain += [("id", "!=", partner_id)] partner.same_dni_partner_id = Partner.search(domain, limit=1) (self - filtered_partners).same_dni_partner_id = False - @api.constrains('current_main_group_id') + @api.constrains("current_main_group_id") def _check_unique_main_group_per_year(self): """La validación se realiza sobre el campo `current_main_group_id` en lugar de `student_group_ids` porque, al usar un campo many2many, la validación no se activaba al agregar un estudiante directamente desde un grupo. """ for partner in self: - domain = [ - ('student_ids', '=', partner.id), - ('subject_id', '=', False) - ] - grouped_data = self.env['academic.group'].read_group( - domain, - ['year'], - ['year'] - ) - duplicate_years = [group['year'] for group in grouped_data if group['year_count'] > 1] + domain = [("student_ids", "=", partner.id), ("subject_id", "=", False)] + grouped_data = self.env["academic.group"].read_group(domain, ["year"], ["year"]) + duplicate_years = [group["year"] for group in grouped_data if group["year_count"] > 1] if duplicate_years: - raise ValidationError(_( - "The partner '%s' cannot belong to multiple groups " - "without a subject in the same year. Conflicting year(s): %s." - ) % ( - partner.name, - ', '.join(map(str, duplicate_years)) - )) - - @api.depends('student_group_ids') + raise ValidationError( + _( + "The partner '%s' cannot belong to multiple groups " + "without a subject in the same year. Conflicting year(s): %s." + ) + % (partner.name, ", ".join(map(str, duplicate_years))) + ) + + @api.depends("student_group_ids") def _compute_current_main_group(self): for rec in self: student_group = rec.student_group_ids.filtered(lambda g: g.year == date.today().year and not g.subject_id) @@ -224,6 +239,6 @@ def _compute_current_main_group(self): @api.model def get_payment_responsible(self): self.ensure_one() - return self.student_link_ids.filtered( - lambda x: self.env.ref('academic.paying_role') in x.role_ids - ).mapped('partner_id') + return self.student_link_ids.filtered(lambda x: self.env.ref("academic.paying_role") in x.role_ids).mapped( + "partner_id" + ) diff --git a/academic/models/res_partner_category.py b/academic/models/res_partner_category.py index 1f861fa1..5d64105f 100644 --- a/academic/models/res_partner_category.py +++ b/academic/models/res_partner_category.py @@ -2,11 +2,11 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResPartnerCategory(models.Model): - _inherit = 'res.partner.category' + _inherit = "res.partner.category" _check_company_domain = models.check_company_domain_parent_of - company_id = fields.Many2one('res.company', 'Company', index=True) + company_id = fields.Many2one("res.company", "Company", index=True) diff --git a/academic/models/res_partner_family.py b/academic/models/res_partner_family.py index 584cdd3a..98acf988 100644 --- a/academic/models/res_partner_family.py +++ b/academic/models/res_partner_family.py @@ -2,18 +2,17 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResPartner(models.Model): - - _name = 'res.partner.family' - _description = 'res.partner.family' + _name = "res.partner.family" + _description = "res.partner.family" name = fields.Char(required=True) - student_ids = fields.One2many('res.partner', 'family_id', 'Students') - contact_ids = fields.One2many('res.partner', 'family_id', 'Students') - student_link_ids = fields.One2many('res.partner.link', 'student_id', string='Contactos y Roles') + student_ids = fields.One2many("res.partner", "family_id", "Students") + contact_ids = fields.One2many("res.partner", "family_id", "Students") + student_link_ids = fields.One2many("res.partner.link", "student_id", string="Contactos y Roles") # TODO tal vez agregar un selection de los tipos fuertes y permitir abm? por ahora vamos con los # external ids diff --git a/academic/models/res_partner_link.py b/academic/models/res_partner_link.py index 656b76a4..8b84ae4f 100644 --- a/academic/models/res_partner_link.py +++ b/academic/models/res_partner_link.py @@ -2,25 +2,23 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields, api -from odoo.exceptions import UserError +from odoo import fields, models class ResPartner(models.Model): - - _name = 'res.partner.link' - _description = 'res.partner.link' + _name = "res.partner.link" + _description = "res.partner.link" _check_company_auto = True - _order = 'sequence' + _order = "sequence" _check_company_domain = models.check_company_domain_parent_of - student_id = fields.Many2one('res.partner', 'Student or Family', required=True, ondelete='cascade') - company_id = fields.Many2one(related='student_id.company_id') + student_id = fields.Many2one("res.partner", "Student or Family", required=True, ondelete="cascade") + company_id = fields.Many2one(related="student_id.company_id") # student_id = fields.Many2one('res.partner', 'Student', ondelete='cascade') # family_id = fields.Many2one('res.partner', 'Family', ondelete='cascade') - relationship_id = fields.Many2one('res.partner.relationship', required=True, ondelete='restrict') - role_ids = fields.Many2many('res.partner.role', string='Roles') - partner_id = fields.Many2one('res.partner', required=True, ondelete='restrict', check_company=True) + relationship_id = fields.Many2one("res.partner.relationship", required=True, ondelete="restrict") + role_ids = fields.Many2many("res.partner.role", string="Roles") + partner_id = fields.Many2one("res.partner", required=True, ondelete="restrict", check_company=True) note = fields.Text(string="Notas") sequence = fields.Integer(default=10) @@ -31,6 +29,9 @@ class ResPartner(models.Model): # raise UserError('Los contactos y roles deben estar vinculados a una famila o a un estudiante') _sql_constraints = [ - ('link_unique', - 'unique(student_id, partner_id)', - 'El contacto debe ser agregado por unica vez en cada familia o estudiante')] + ( + "link_unique", + "unique(student_id, partner_id)", + "El contacto debe ser agregado por unica vez en cada familia o estudiante", + ) + ] diff --git a/academic/models/res_partner_relationship.py b/academic/models/res_partner_relationship.py index fc9301b0..b0777ab1 100644 --- a/academic/models/res_partner_relationship.py +++ b/academic/models/res_partner_relationship.py @@ -1,10 +1,9 @@ -from odoo import models, fields +from odoo import fields, models class ResPartnerRelationship(models.Model): - - _name = 'res.partner.relationship' - _description = 'Contacts Relationship' + _name = "res.partner.relationship" + _description = "Contacts Relationship" name = fields.Char( required=True, diff --git a/academic/models/res_partner_role.py b/academic/models/res_partner_role.py index c6e574ce..266c90f7 100644 --- a/academic/models/res_partner_role.py +++ b/academic/models/res_partner_role.py @@ -2,16 +2,17 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResPartner(models.Model): - - _name = 'res.partner.role' - _description = 'res.partner.role' + _name = "res.partner.role" + _description = "res.partner.role" name = fields.Char(required=True) # TODO tal vez agregar un selection de los tipos fuertes y permitir abm? por ahora vamos con los # external ids # name = fields.Char(required=True) - color = fields.Integer('Color Index',) + color = fields.Integer( + "Color Index", + ) diff --git a/academic/models/res_users.py b/academic/models/res_users.py index 56e75713..75afd09f 100644 --- a/academic/models/res_users.py +++ b/academic/models/res_users.py @@ -2,28 +2,26 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, api +from odoo import models class ResUsers(models.Model): - _inherit = 'res.users' + _inherit = "res.users" def _set_encrypted_password(self, uid, pw): - """ Si es estudiante no limpiamos las password, + """Si es estudiante no limpiamos las password, solo guardamos la encriptada TODO deberiamos mejorar y solo guardar la password si venimos desde los groups, es decir forzando contraseña nosotros. Pero tendriamos que en ese caso ademas hacer que el init que borramos no limpie esas pass. """ - if self.has_group('academic.group_portal_student'): - return self.env.cr.execute( - "UPDATE res_users SET password=%s WHERE id=%s", - (pw, uid)) + if self.has_group("academic.group_portal_student"): + return self.env.cr.execute("UPDATE res_users SET password=%s WHERE id=%s", (pw, uid)) return super(ResUsers, self)._set_encrypted_password(uid, pw) def _compute_display_name(self): - if self._context.get('show_login', False): + if self._context.get("show_login", False): for rec in self: rec.display_name = rec.login super()._compute_display_name() diff --git a/academic/models/sale_order.py b/academic/models/sale_order.py index 62d2bd2c..c8d2f225 100644 --- a/academic/models/sale_order.py +++ b/academic/models/sale_order.py @@ -2,29 +2,30 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import api, models, fields, _ +from odoo import api, fields, models class SaleOrder(models.Model): + _inherit = "sale.order" - _inherit = 'sale.order' - - partner_id = fields.Many2one(domain="[('type', '!=', 'private'), ('company_id', 'in', (False, company_id)), ('partner_type', '=', 'student')]") - partner_invoice_ids = fields.Many2many('res.partner', compute='_compute_partner_invoice') + partner_id = fields.Many2one( + domain="[('type', '!=', 'private'), ('company_id', 'in', (False, company_id)), ('partner_type', '=', 'student')]" + ) + partner_invoice_ids = fields.Many2many("res.partner", compute="_compute_partner_invoice") # dejamos solo depends a partner_id para que si cambia algo de la asignación no se re-calculen todas las ventas existentes - @api.depends('partner_id') + @api.depends("partner_id") def _compute_partner_invoice(self): - orders = self.filtered('partner_id') + orders = self.filtered("partner_id") for rec in orders: rec.partner_invoice_ids = rec.partner_id.get_payment_responsible() (self - orders).partner_invoice_ids = False - @api.depends('partner_invoice_ids') + @api.depends("partner_invoice_ids") def _compute_partner_invoice_id(self): # si bien en el dominio solo permitimos estudiantes, para no romper demo data de odoo ni tests, si no es un estudiante # dejamos compute by super - students_orders = self.filtered(lambda x: x.partner_id.partner_type == 'student') + students_orders = self.filtered(lambda x: x.partner_id.partner_type == "student") for order in students_orders: order.partner_invoice_id = order.partner_invoice_ids._origin[:1] super(SaleOrder, self - students_orders)._compute_partner_invoice_id() @@ -36,15 +37,17 @@ def _prepare_invoice(self): def action_confirm(self): for rec in self: - rec.message_subscribe([ - payment_responsible.id - for payment_responsible in rec.partner_invoice_id | rec.partner_invoice_ids - if payment_responsible not in rec.sudo().message_partner_ids - ]) + rec.message_subscribe( + [ + payment_responsible.id + for payment_responsible in rec.partner_invoice_id | rec.partner_invoice_ids + if payment_responsible not in rec.sudo().message_partner_ids + ] + ) return super().action_confirm() def _message_get_default_recipients(self): - """ Por defecto las plantillas mandan a partner_id pero para nosotros el partners es el estudiante. + """Por defecto las plantillas mandan a partner_id pero para nosotros el partners es el estudiante. Cambiamos plantillas para que usen el campo "use_default_to" y luego cae en este método de python donde podemos ir mejorando a medida que nos pidan y modificar la logica de recipients. Por ahora lo mandamos solo al partner de facturación si está definido @@ -54,8 +57,8 @@ def _message_get_default_recipients(self): payment_responsible = record.partner_invoice_id | record.partner_invoice_ids if payment_responsible: default_recipients[record.id] = { - 'email_cc': False, - 'email_to': False, - 'partner_ids': payment_responsible.ids, + "email_cc": False, + "email_to": False, + "partner_ids": payment_responsible.ids, } return default_recipients diff --git a/academic/wizards/account_move_send.py b/academic/wizards/account_move_send.py index bf9e1506..c6383df0 100644 --- a/academic/wizards/account_move_send.py +++ b/academic/wizards/account_move_send.py @@ -6,8 +6,8 @@ class AccountMoveSend(models.AbstractModel): - _inherit = 'account.move.send' + _inherit = "account.move.send" def _get_default_mail_partner_ids(self, move, mail_template, mail_lang): - partners_invoice = move.student_id.get_payment_responsible() if move.student_id else self.env['res.partner'] + partners_invoice = move.student_id.get_payment_responsible() if move.student_id else self.env["res.partner"] return super()._get_default_mail_partner_ids(move, mail_template, mail_lang) | partners_invoice diff --git a/academic/wizards/portal_wizard.py b/academic/wizards/portal_wizard.py index 5928ccd9..f8648e08 100644 --- a/academic/wizards/portal_wizard.py +++ b/academic/wizards/portal_wizard.py @@ -1,39 +1,33 @@ -from odoo import fields, api, models +from odoo import api, fields, models class PortalWizard(models.TransientModel): - _inherit = 'portal.wizard' + _inherit = "portal.wizard" @api.model def _default_portal(self): - active_model = self._context.get('active_model') - active_id = self._context.get('active_id') - if active_model == 'res.partner' and active_id: - partner_type = self.env[ - active_model].browse(active_id).partner_type - groups = self.env['res.groups'] - if partner_type == 'gral_administrator': - groups = self.env.ref( - 'academic.group_portal_gral_administrator') - elif partner_type == 'administrator': - groups = self.env.ref( - 'academic.group_portal_administrator') - elif partner_type == 'teacher': - groups = self.env.ref( - 'academic.group_portal_teacher') - elif partner_type == 'parent': - groups = self.env.ref( - 'academic.group_portal_parent') - elif partner_type == 'student': - groups = self.env.ref( - 'academic.group_portal_student') + active_model = self._context.get("active_model") + active_id = self._context.get("active_id") + if active_model == "res.partner" and active_id: + partner_type = self.env[active_model].browse(active_id).partner_type + groups = self.env["res.groups"] + if partner_type == "gral_administrator": + groups = self.env.ref("academic.group_portal_gral_administrator") + elif partner_type == "administrator": + groups = self.env.ref("academic.group_portal_administrator") + elif partner_type == "teacher": + groups = self.env.ref("academic.group_portal_teacher") + elif partner_type == "parent": + groups = self.env.ref("academic.group_portal_parent") + elif partner_type == "student": + groups = self.env.ref("academic.group_portal_student") return groups.id return super(PortalWizard, self)._default_portal() - portal_id = fields.Many2one('res.groups', default=_default_portal) + portal_id = fields.Many2one("res.groups", default=_default_portal) def action_grant_access_all(self): for wizard_user in self.user_ids: - if not wizard_user.is_portal and not wizard_user.is_internal and wizard_user.email_state == 'ok': + if not wizard_user.is_portal and not wizard_user.is_internal and wizard_user.email_state == "ok": wizard_user.action_grant_access() return self._action_open_modal() diff --git a/academic/wizards/portal_wizard_user.py b/academic/wizards/portal_wizard_user.py index 5102709f..625d31e8 100644 --- a/academic/wizards/portal_wizard_user.py +++ b/academic/wizards/portal_wizard_user.py @@ -1,12 +1,12 @@ -from odoo import models, _ -from odoo.tools import email_normalize -from odoo.tools import ustr import re import unicodedata +from odoo import _, models +from odoo.tools import email_normalize, ustr + class PortalWizardUser(models.TransientModel): - _inherit = 'portal.wizard.user' + _inherit = "portal.wizard.user" def _clean_and_make_unique(self, name): # when an alias name appears to already be an email, we keep the local @@ -16,24 +16,22 @@ def remove_accents(input_str): latin letters by an ASCII equivalent. Will obviously change the meaning of input_str and work only for some cases""" input_str = ustr(input_str) - nkfd_form = unicodedata.normalize('NFKD', input_str) - return u''.join( - [c for c in nkfd_form if not unicodedata.combining(c)]) + nkfd_form = unicodedata.normalize("NFKD", input_str) + return "".join([c for c in nkfd_form if not unicodedata.combining(c)]) - name = remove_accents(name).lower().split('@')[0] - name = re.sub(r'[^\w+.]+', '.', name) + name = remove_accents(name).lower().split("@")[0] + name = re.sub(r"[^\w+.]+", ".", name) return self._find_unique(name) def _find_unique(self, name): """Find a unique alias name similar to ``name``. If ``name`` is - already taken, make a variant by adding an integer suffix until - an unused alias is found. + already taken, make a variant by adding an integer suffix until + an unused alias is found. """ sequence = None while True: - new_name = "%s%s" % ( - name, sequence) if sequence is not None else name - if not self.env['res.users'].search([('login', '=', new_name)]): + new_name = "%s%s" % (name, sequence) if sequence is not None else name + if not self.env["res.users"].search([("login", "=", new_name)]): break sequence = (sequence + 1) if sequence else 2 return new_name @@ -48,28 +46,27 @@ def get_error_messages(self): return res def _create_user(self): - """ we overwrite this method because its not inheritable - """ + """we overwrite this method because its not inheritable""" vals = { - 'partner_id': self.partner_id.id, - 'company_id': self.env.company.id, - 'company_ids': [(6, 0, self.env.company.ids)], - 'groups_id': [(6, 0, [])], + "partner_id": self.partner_id.id, + "company_id": self.env.company.id, + "company_ids": [(6, 0, self.env.company.ids)], + "groups_id": [(6, 0, [])], } if self.partner_id.email: - vals['login'] = email_normalize(self.partner_id.email) - vals['email'] = email_normalize(self.partner_id.email) + vals["login"] = email_normalize(self.partner_id.email) + vals["email"] = email_normalize(self.partner_id.email) else: - vals['login'] = self._clean_and_make_unique(self.partner_id.name) - return self.env['res.users'].with_context(no_reset_password=True).create(vals) + vals["login"] = self._clean_and_make_unique(self.partner_id.name) + return self.env["res.users"].with_context(no_reset_password=True).create(vals) def _get_group_by_partner_type(self): group_mapping = { - 'gral_administrator': 'academic.group_portal_gral_administrator', - 'administrator': 'academic.group_portal_administrator', - 'teacher': 'academic.group_portal_teacher', - 'parent': 'academic.group_portal_parent', - 'student': 'academic.group_portal_student', + "gral_administrator": "academic.group_portal_gral_administrator", + "administrator": "academic.group_portal_administrator", + "teacher": "academic.group_portal_teacher", + "parent": "academic.group_portal_parent", + "student": "academic.group_portal_student", } group_ref = group_mapping.get(self.partner_id.partner_type) if group_ref: @@ -79,7 +76,7 @@ def _get_group_by_partner_type(self): def action_grant_access(self): result = super().action_grant_access() partner_group = self._get_group_by_partner_type() - portal_backend_group = self.env.ref('portal_backend.group_portal_backend') + portal_backend_group = self.env.ref("portal_backend.group_portal_backend") if partner_group and portal_backend_group: - self.user_id.write({'groups_id': [(4, partner_group.id), (4, portal_backend_group.id)]}) + self.user_id.write({"groups_id": [(4, partner_group.id), (4, portal_backend_group.id)]}) return result diff --git a/academic_account_interests/__manifest__.py b/academic_account_interests/__manifest__.py index ca893908..7a63892f 100644 --- a/academic_account_interests/__manifest__.py +++ b/academic_account_interests/__manifest__.py @@ -18,21 +18,16 @@ # ############################################################################## { - 'name': 'Academic Account Interests', - 'version': "18.0.1.0.0", - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'academic', - 'account_interests' - ], - 'data': [ - - ], - 'installable': True, - 'auto_install': True, - 'application': False, + "name": "Academic Account Interests", + "version": "18.0.1.0.0", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": ["academic", "account_interests"], + "data": [], + "installable": True, + "auto_install": True, + "application": False, } diff --git a/academic_account_interests/models/res_company_interest.py b/academic_account_interests/models/res_company_interest.py index 3873897a..9d46ee0d 100644 --- a/academic_account_interests/models/res_company_interest.py +++ b/academic_account_interests/models/res_company_interest.py @@ -5,85 +5,96 @@ from odoo import models from odoo.tools.safe_eval import safe_eval + class ResCompanyInterest(models.Model): - _inherit = 'res.company.interest' + _inherit = "res.company.interest" def _prepare_interest_invoice(self, partner, debt, to_date, journal): student = partner - partner = debt['partner_id'] + partner = debt["partner_id"] res = super()._prepare_interest_invoice(partner, debt, to_date, journal) if res: - res.update({ - 'student_id': student.id, - 'partner_id': partner.id, - }) + res.update( + { + "student_id": student.id, + "partner_id": partner.id, + } + ) return res def _calculate_debts(self, from_date, to_date, groupby=None): if groupby is None: - groupby = ['student_id', 'partner_id'] + groupby = ["student_id", "partner_id"] deuda = {} interest_rate = { - 'daily': 1, - 'weekly': 7, - 'monthly': 30, - 'yearly': 360, + "daily": 1, + "weekly": 7, + "monthly": 30, + "yearly": 360, } # Deudas de períodos anteriores - previous_grouped_lines = self.env['account.move.line']._read_group( - domain=self._get_move_line_domains() + [('full_reconcile_id', '=', False), ('date_maturity', '<', from_date)], + previous_grouped_lines = self.env["account.move.line"]._read_group( + domain=self._get_move_line_domains() + + [("full_reconcile_id", "=", False), ("date_maturity", "<", from_date)], groupby=groupby, - aggregates=['amount_residual:sum'], + aggregates=["amount_residual:sum"], ) for x in previous_grouped_lines: - self._update_deuda(deuda, x[0], 'Deuda periodos anteriores', x[2] * self.rate) - deuda[x[0]]['partner_id'] = x[1] + self._update_deuda(deuda, x[0], "Deuda periodos anteriores", x[2] * self.rate) + deuda[x[0]]["partner_id"] = x[1] # Intereses por el último período - last_period_lines = self.env['account.move.line'].search( - self._get_move_line_domains() + [('amount_residual', '>', 0), ('date_maturity', '>=', from_date), ('date_maturity', '<', to_date)] + last_period_lines = self.env["account.move.line"].search( + self._get_move_line_domains() + + [("amount_residual", ">", 0), ("date_maturity", ">=", from_date), ("date_maturity", "<", to_date)] ) - for student, amls in last_period_lines.grouped('student_id').items(): + for student, amls in last_period_lines.grouped("student_id").items(): interest = sum( - move.amount_residual * ((to_date - move.invoice_date_due).days - 1) * (self.rate / interest_rate[self.rule_type]) - for move, lines in amls.grouped('move_id').items() + move.amount_residual + * ((to_date - move.invoice_date_due).days - 1) + * (self.rate / interest_rate[self.rule_type]) + for move, lines in amls.grouped("move_id").items() ) - self._update_deuda(deuda, student, 'Deuda último periodo', interest) - deuda[student]['partner_id'] = amls[0].partner_id + self._update_deuda(deuda, student, "Deuda último periodo", interest) + deuda[student]["partner_id"] = amls[0].partner_id # Intereses por pagos tardíos if self.late_payment_interest: - partial_domain = [ # lo dejamos para NTH # debit_move_id. safe eval domain - ('debit_move_id.partner_id.active', '=', True), - ('debit_move_id.parent_state', '=', 'posted'), - ('debit_move_id.account_id', 'in', self.receivable_account_ids.ids), - ('credit_move_id.date', '>=', from_date), - ('credit_move_id.date', '<', to_date)] - + ("debit_move_id.partner_id.active", "=", True), + ("debit_move_id.parent_state", "=", "posted"), + ("debit_move_id.account_id", "in", self.receivable_account_ids.ids), + ("credit_move_id.date", ">=", from_date), + ("credit_move_id.date", "<", to_date), + ] + if self.domain: - partial_domain.append(('debit_move_id', 'any', safe_eval(self.domain))) - - partials = self.env['account.partial.reconcile'].search(partial_domain).filtered(lambda x: x.credit_move_id.date > x.debit_move_id.date_maturity).grouped('debit_move_id') - + partial_domain.append(("debit_move_id", "any", safe_eval(self.domain))) + + partials = ( + self.env["account.partial.reconcile"] + .search(partial_domain) + .filtered(lambda x: x.credit_move_id.date > x.debit_move_id.date_maturity) + .grouped("debit_move_id") + ) + for move_line, parts in partials.items(): due_date = max(from_date, parts.debit_move_id.date_maturity) days = (parts.credit_move_id.date - due_date).days interest = parts.amount * days * (self.rate / interest_rate[self.rule_type]) - #Se debe actualiza la deuda del partner, por ello se llama al cliente metodo para su actualizacion - self._update_deuda(deuda, move_line.student_id, 'Deuda pagos vencidos', interest) - deuda[move_line.student_id]['partner_id'] = move_line.partner_id - + # Se debe actualiza la deuda del partner, por ello se llama al cliente metodo para su actualizacion + self._update_deuda(deuda, move_line.student_id, "Deuda pagos vencidos", interest) + deuda[move_line.student_id]["partner_id"] = move_line.partner_id return deuda def _get_move_line_domains(self): res = super()._get_move_line_domains() - res += [('journal_id.type', '=', 'sale')] + res += [("journal_id.type", "=", "sale")] return res diff --git a/academic_partner_code/__manifest__.py b/academic_partner_code/__manifest__.py index a4004992..50f39fce 100644 --- a/academic_partner_code/__manifest__.py +++ b/academic_partner_code/__manifest__.py @@ -18,21 +18,16 @@ # ############################################################################## { - 'name': 'Academic Partner Code', - 'version': "18.0.1.0.0", - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'academic' - ], - 'data': [ - 'data/ir_sequence_data.xml', - 'views/res_partner_views.xml' - ], - 'installable': True, - 'auto_install': False, - 'application': False, + "name": "Academic Partner Code", + "version": "18.0.1.0.0", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": ["academic"], + "data": ["data/ir_sequence_data.xml", "views/res_partner_views.xml"], + "installable": True, + "auto_install": False, + "application": False, } diff --git a/academic_partner_code/models/res_partner.py b/academic_partner_code/models/res_partner.py index e1ffcacc..4b9b8f57 100644 --- a/academic_partner_code/models/res_partner.py +++ b/academic_partner_code/models/res_partner.py @@ -2,22 +2,25 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api +from odoo import api, fields, models class Partner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" - academic_code = fields.Char(copy=False, index='btree_not_null') + academic_code = fields.Char(copy=False, index="btree_not_null") @api.model_create_multi def create(self, vals_list): for vals in vals_list: - if vals.get('partner_type') == 'family': - vals['academic_code'] = self.env['ir.sequence'].next_by_code('partner.academic.code') + if vals.get("partner_type") == "family": + vals["academic_code"] = self.env["ir.sequence"].next_by_code("partner.academic.code") return super().create(vals_list) _sql_constraints = [ - ('academic_code_company_uniq', 'unique(academic_code, company_id)', - 'The Academic Code must be unique within the same company!') + ( + "academic_code_company_uniq", + "unique(academic_code, company_id)", + "The Academic Code must be unique within the same company!", + ) ] diff --git a/academic_sale_subscription/README.rst b/academic_sale_subscription/README.rst index 7fd6ce2b..f9098913 100644 --- a/academic_sale_subscription/README.rst +++ b/academic_sale_subscription/README.rst @@ -67,4 +67,4 @@ Maintainer This module is maintained by the |company|. -To contribute to this module, please visit https://www.adhoc.com.ar. \ No newline at end of file +To contribute to this module, please visit https://www.adhoc.com.ar. diff --git a/academic_sale_subscription/__manifest__.py b/academic_sale_subscription/__manifest__.py index a804ecf3..e36b30e5 100644 --- a/academic_sale_subscription/__manifest__.py +++ b/academic_sale_subscription/__manifest__.py @@ -1,26 +1,23 @@ # © 2016 ADHOC SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Academic Sale Subscription', - 'version': "18.0.1.0.0", - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'academic', - 'sale_subscription_ux' + "name": "Academic Sale Subscription", + "version": "18.0.1.0.0", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": ["academic", "sale_subscription_ux"], + "data": [ + "security/ir.model.access.csv", + "views/sale_order_template_views.xml", + "views/sale_subscription_plan_views.xml", + "wizard/academic_order_wizard_views.xml", + "views/res_partner_views.xml", + "views/sale_subscription_views.xml", ], - 'data': [ - 'security/ir.model.access.csv', - 'views/sale_order_template_views.xml', - 'views/sale_subscription_plan_views.xml', - 'wizard/academic_order_wizard_views.xml', - 'views/res_partner_views.xml', - 'views/sale_subscription_views.xml', - ], - 'installable': True, - 'auto_install': False, - 'application': False, + "installable": True, + "auto_install": False, + "application": False, } diff --git a/academic_sale_subscription/models/res_partner.py b/academic_sale_subscription/models/res_partner.py index 321db816..3d21d087 100644 --- a/academic_sale_subscription/models/res_partner.py +++ b/academic_sale_subscription/models/res_partner.py @@ -2,22 +2,23 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models class ResPartner(models.Model): - _inherit = 'res.partner' + _inherit = "res.partner" current_subscription_ids = fields.One2many( - 'sale.order.line', - compute='_compute_current_subscription', + "sale.order.line", + compute="_compute_current_subscription", ) def _compute_current_subscription(self): for rec in self: - rec.current_subscription_ids = self.env['sale.order.line'].search([ - ('order_partner_id', '=', rec.id), - ('order_id.subscription_state', '=', '3_progress') - ]).filtered( - lambda line: not line.order_id.end_date or line.order_id.next_invoice_date < line.order_id.end_date + rec.current_subscription_ids = ( + self.env["sale.order.line"] + .search([("order_partner_id", "=", rec.id), ("order_id.subscription_state", "=", "3_progress")]) + .filtered( + lambda line: not line.order_id.end_date or line.order_id.next_invoice_date < line.order_id.end_date + ) ) diff --git a/academic_sale_subscription/models/sale_order.py b/academic_sale_subscription/models/sale_order.py index 9239c83c..65c8005d 100644 --- a/academic_sale_subscription/models/sale_order.py +++ b/academic_sale_subscription/models/sale_order.py @@ -2,39 +2,54 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, api, fields from dateutil.relativedelta import relativedelta +from odoo import api, fields, models from odoo.exceptions import UserError class SaleOrder(models.Model): _inherit = "sale.order" - current_main_group_id = fields.Many2one('academic.group', related='partner_id.current_main_group_id') + current_main_group_id = fields.Many2one("academic.group", related="partner_id.current_main_group_id") def _set_deferred_end_date_from_template(self): self.ensure_one() - if (not self.sale_order_template_id or self.sale_order_template_id.is_unlimited) and self.plan_id and not self.plan_id.is_unlimited: - self.write({'end_date': self.next_invoice_date + self.plan_id.duration - relativedelta(days=1)}) + if ( + (not self.sale_order_template_id or self.sale_order_template_id.is_unlimited) + and self.plan_id + and not self.plan_id.is_unlimited + ): + self.write({"end_date": self.next_invoice_date + self.plan_id.duration - relativedelta(days=1)}) else: super()._set_deferred_end_date_from_template() - @api.constrains('sale_order_template_id', 'plan_id') + @api.constrains("sale_order_template_id", "plan_id") def _check_period(self): for rec in self: - if rec.sale_order_template_id and not rec.sale_order_template_id.is_unlimited and rec.plan_id and not rec.plan_id.is_unlimited: - raise UserError(self.env._('There cannot be a sale order template and a recurring plan both with a defined period.')) + if ( + rec.sale_order_template_id + and not rec.sale_order_template_id.is_unlimited + and rec.plan_id + and not rec.plan_id.is_unlimited + ): + raise UserError( + self.env._("There cannot be a sale order template and a recurring plan both with a defined period.") + ) def _get_auto_invoice_grouping_keys(self): - grouping_keys = super()._get_auto_invoice_grouping_keys() + ['partner_id', 'partner_invoice_id', 'payment_term_id'] + grouping_keys = super()._get_auto_invoice_grouping_keys() + [ + "partner_id", + "partner_invoice_id", + "payment_term_id", + ] grouping_keys = list(set(grouping_keys)) return grouping_keys def action_update_prices(self): - if self._context.get('action_update_subscription_prices'): + if self._context.get("action_update_subscription_prices"): lines_no_recurring_pricing = self.order_line.filtered( lambda line: not bool( - self.env['sale.subscription.pricing']._get_first_suitable_recurring_pricing( + self.env["sale.subscription.pricing"]._get_first_suitable_recurring_pricing( line.product_id, line.plan_id, line.order_id.pricelist_id ) ) diff --git a/academic_sale_subscription/models/sale_order_line.py b/academic_sale_subscription/models/sale_order_line.py index c9af4bca..879a8ed7 100644 --- a/academic_sale_subscription/models/sale_order_line.py +++ b/academic_sale_subscription/models/sale_order_line.py @@ -8,15 +8,15 @@ class SaleOrderLine(models.Model): _inherit = "sale.order.line" - partner_invoice_id = fields.Many2one(related='order_id.partner_invoice_id') - payment_term_id = fields.Many2one(related='order_id.payment_term_id') + partner_invoice_id = fields.Many2one(related="order_id.partner_invoice_id") + payment_term_id = fields.Many2one(related="order_id.payment_term_id") def open_subscription_form(self): return { - 'type': 'ir.actions.act_window', - 'res_model': 'sale.order', - 'target': 'current', - 'view_mode': 'form', - 'res_id': self.order_id.id, - 'context': dict(self._context), + "type": "ir.actions.act_window", + "res_model": "sale.order", + "target": "current", + "view_mode": "form", + "res_id": self.order_id.id, + "context": dict(self._context), } diff --git a/academic_sale_subscription/models/sale_subscription_plan.py b/academic_sale_subscription/models/sale_subscription_plan.py index 974ec9c4..dbcd47bc 100644 --- a/academic_sale_subscription/models/sale_subscription_plan.py +++ b/academic_sale_subscription/models/sale_subscription_plan.py @@ -2,20 +2,22 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields +from odoo import fields, models from odoo.tools import get_timedelta class SaleSubscriptionPlan(models.Model): - _inherit = 'sale.subscription.plan' + _inherit = "sale.subscription.plan" _sql_constraints = [ - ('check_duration_value', 'CHECK(is_unlimited OR duration_value > 0)', 'The duration can\'t be negative or 0.'), + ("check_duration_value", "CHECK(is_unlimited OR duration_value > 0)", "The duration can't be negative or 0."), ] - is_unlimited = fields.Boolean('Last Forever', default=True) + is_unlimited = fields.Boolean("Last Forever", default=True) duration_value = fields.Integer(string="End After", default=1, required=True) - duration_unit = fields.Selection([('month', 'Months'), ('year', 'Years')], help="Contract duration", default='month', required=True) + duration_unit = fields.Selection( + [("month", "Months"), ("year", "Years")], help="Contract duration", default="month", required=True + ) @property def duration(self): diff --git a/academic_sale_subscription/wizard/academic_order_wizard.py b/academic_sale_subscription/wizard/academic_order_wizard.py index d4ec80e0..664624d3 100644 --- a/academic_sale_subscription/wizard/academic_order_wizard.py +++ b/academic_sale_subscription/wizard/academic_order_wizard.py @@ -2,87 +2,110 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import models, fields, api, Command -from odoo.exceptions import ValidationError, UserError +from odoo import Command, api, fields, models +from odoo.exceptions import UserError, ValidationError class OrderWizard(models.TransientModel): - _name = 'academic.order.wizard' - _description = 'Academic Order Wizard' - - student_ids = fields.Many2many('res.partner', default=lambda self: self.env.context.get('active_ids', [])) - plan_id = fields.Many2one('sale.subscription.plan', required=True, compute="_compute_plan", readonly=False, store=True) - order_wizard_line_ids = fields.One2many('academic.order.wizard.line', 'academic_order_wizard_id', compute="_compute_order_wizard_line", readonly=False, store=True) - pricelist_id = fields.Many2one('product.pricelist') - template_id = fields.Many2one('sale.order.template') + _name = "academic.order.wizard" + _description = "Academic Order Wizard" + + student_ids = fields.Many2many("res.partner", default=lambda self: self.env.context.get("active_ids", [])) + plan_id = fields.Many2one( + "sale.subscription.plan", required=True, compute="_compute_plan", readonly=False, store=True + ) + order_wizard_line_ids = fields.One2many( + "academic.order.wizard.line", + "academic_order_wizard_id", + compute="_compute_order_wizard_line", + readonly=False, + store=True, + ) + pricelist_id = fields.Many2one("product.pricelist") + template_id = fields.Many2one("sale.order.template") next_invoice_date = fields.Date(required=True) - status_sale = fields.Selection([ - ('draft', 'Draft'), - ('confirmed', 'Confirmed')], - default='draft', help='Status to be given to sales orders.' + status_sale = fields.Selection( + [("draft", "Draft"), ("confirmed", "Confirmed")], default="draft", help="Status to be given to sales orders." ) validity_date = fields.Date() - payment_term_id = fields.Many2one('account.payment.term') - academic_group_id = fields.Many2one('academic.group') + payment_term_id = fields.Many2one("account.payment.term") + academic_group_id = fields.Many2one("academic.group") def action_create_mass_subscription(self): if not self.student_ids: raise ValidationError(self.env._("No student has been selected.")) - if partners := self.student_ids.filtered(lambda x: x.partner_type != 'student'): - raise ValidationError(self.env._("The contacts must be of student type. The following do not meet this condition:\n%s") % "\n".join(partners.mapped('name'))) + if partners := self.student_ids.filtered(lambda x: x.partner_type != "student"): + raise ValidationError( + self.env._("The contacts must be of student type. The following do not meet this condition:\n%s") + % "\n".join(partners.mapped("name")) + ) if partners := self.student_ids.filtered( - lambda x: not x.get_payment_responsible() or not x.get_payment_responsible().filtered('active') + lambda x: not x.get_payment_responsible() or not x.get_payment_responsible().filtered("active") ): - raise ValidationError(self.env._("The following students either have no payment responsible assigned or their payment responsible is archived:\n%s", "\n".join(partners.mapped('name')))) + raise ValidationError( + self.env._( + "The following students either have no payment responsible assigned or their payment responsible is archived:\n%s", + "\n".join(partners.mapped("name")), + ) + ) subscriptions = self._create_mass_subscription() - action = self.env.ref('sale_subscription.sale_subscription_action').read()[0] - action.update({ - 'domain': [('id', 'in', subscriptions.ids)], - 'views': sorted(action['views'], key=lambda v: v[1] != 'list'), # show list view first - 'context': {'default_is_subscription': 1} - }) + action = self.env.ref("sale_subscription.sale_subscription_action").read()[0] + action.update( + { + "domain": [("id", "in", subscriptions.ids)], + "views": sorted(action["views"], key=lambda v: v[1] != "list"), # show list view first + "context": {"default_is_subscription": 1}, + } + ) return action def _create_mass_subscription(self): - subscriptions = self.env['sale.order'] + subscriptions = self.env["sale.order"] for student in self.student_ids: - subscription = self.env['sale.order'].create({ - 'partner_id': student.id, - 'plan_id': self.plan_id.id, - 'pricelist_id': self.pricelist_id.id, - 'next_invoice_date': self.next_invoice_date, - 'sale_order_template_id': self.template_id.id, - 'validity_date': self.validity_date if self.status_sale == 'draft' else False, - **({'payment_term_id': self.payment_term_id.id} if self.payment_term_id else {}), - 'order_line': [ - (0, 0, { - 'product_id': line.product_id.id, - 'product_uom_qty': line.quantity, - 'price_unit': line.price, - **({'name': line.description} if line.description else {}), - }) for line in self.order_wizard_line_ids - ] - }) + subscription = self.env["sale.order"].create( + { + "partner_id": student.id, + "plan_id": self.plan_id.id, + "pricelist_id": self.pricelist_id.id, + "next_invoice_date": self.next_invoice_date, + "sale_order_template_id": self.template_id.id, + "validity_date": self.validity_date if self.status_sale == "draft" else False, + **({"payment_term_id": self.payment_term_id.id} if self.payment_term_id else {}), + "order_line": [ + ( + 0, + 0, + { + "product_id": line.product_id.id, + "product_uom_qty": line.quantity, + "price_unit": line.price, + **({"name": line.description} if line.description else {}), + }, + ) + for line in self.order_wizard_line_ids + ], + } + ) if self.academic_group_id: self.academic_group_id.student_ids = [Command.link(student.id)] - if self.status_sale == 'confirmed': + if self.status_sale == "confirmed": subscription.action_confirm() subscriptions += subscription return subscriptions - @api.depends('template_id') + @api.depends("template_id") def _compute_plan(self): for rec in self: rec.plan_id = rec.template_id.plan_id or False - @api.depends('template_id', 'plan_id', 'pricelist_id') + @api.depends("template_id", "plan_id", "pricelist_id") def _compute_order_wizard_line(self): for rec in self: order_wizard_lines = [] @@ -92,69 +115,73 @@ def _compute_order_wizard_line(self): existing_line = rec.order_wizard_line_ids.filtered(lambda l: l.product_id.id == line.product_id.id) if existing_line: - pricing = self.env['sale.subscription.pricing']._get_first_suitable_recurring_pricing(existing_line.product_id, rec.plan_id, rec.pricelist_id) + pricing = self.env["sale.subscription.pricing"]._get_first_suitable_recurring_pricing( + existing_line.product_id, rec.plan_id, rec.pricelist_id + ) order_wizard_line_vals = { - 'price': pricing.price if pricing else 0.0, + "price": pricing.price if pricing else 0.0, } existing_line.write(order_wizard_line_vals) else: - pricing = self.env['sale.subscription.pricing']._get_first_suitable_recurring_pricing(line.product_id, rec.plan_id, rec.pricelist_id) + pricing = self.env["sale.subscription.pricing"]._get_first_suitable_recurring_pricing( + line.product_id, rec.plan_id, rec.pricelist_id + ) order_wizard_line_vals = { - 'product_id': line.product_id.id, - 'price': pricing.price if pricing else 0.0, - 'quantity': line.product_uom_qty, + "product_id": line.product_id.id, + "price": pricing.price if pricing else 0.0, + "quantity": line.product_uom_qty, } order_wizard_lines.append(Command.create(order_wizard_line_vals)) rec.order_wizard_line_ids = order_wizard_lines - products = rec.template_id.sale_order_template_line_ids.mapped('product_id') + products = rec.template_id.sale_order_template_line_ids.mapped("product_id") for wizard_line in rec.order_wizard_line_ids.filtered(lambda x: x.product_id not in products): - pricing = self.env['sale.subscription.pricing']._get_first_suitable_recurring_pricing(wizard_line.product_id, rec.plan_id, rec.pricelist_id) - wizard_line.write({ - 'price': pricing.price if pricing else 0.0 - }) + pricing = self.env["sale.subscription.pricing"]._get_first_suitable_recurring_pricing( + wizard_line.product_id, rec.plan_id, rec.pricelist_id + ) + wizard_line.write({"price": pricing.price if pricing else 0.0}) - @api.constrains('next_invoice_date', 'validity_date') + @api.constrains("next_invoice_date", "validity_date") def _check_validity_date(self): - for rec in self.filtered(lambda x: x.status_sale == 'draft'): + for rec in self.filtered(lambda x: x.status_sale == "draft"): if rec.next_invoice_date < rec.validity_date: raise UserError(self.env._("The date of the next invoice cannot be earlier than the validity date.")) - @api.constrains('order_wizard_line_ids') + @api.constrains("order_wizard_line_ids") def _check_price(self): for rec in self: if not rec.order_wizard_line_ids: raise ValidationError(self.env._("There must be product lines.")) - @api.onchange('order_wizard_line_ids') + @api.onchange("order_wizard_line_ids") def _onchange_notification_price(self): for rec in self: if rec.order_wizard_line_ids.filtered(lambda x: x.price <= 0): return { - 'warning': { - 'title': self.env._("Warning"), - 'message': self.env._("There are products with price 0."), - 'type': 'notification', + "warning": { + "title": self.env._("Warning"), + "message": self.env._("There are products with price 0."), + "type": "notification", } } class AcademicOrderWizardLine(models.TransientModel): - _name = 'academic.order.wizard.line' - _description = 'Academic Order Wizard Lines' + _name = "academic.order.wizard.line" + _description = "Academic Order Wizard Lines" - academic_order_wizard_id = fields.Many2one('academic.order.wizard') - product_id = fields.Many2one('product.product', required=True) + academic_order_wizard_id = fields.Many2one("academic.order.wizard") + product_id = fields.Many2one("product.product", required=True) price = fields.Monetary(compute="_compute_price", readonly=False, store=True) - currency_id = fields.Many2one('res.currency', default=lambda self: self.env.company.currency_id) + currency_id = fields.Many2one("res.currency", default=lambda self: self.env.company.currency_id) description = fields.Text() quantity = fields.Float(default=1.0) - @api.depends('product_id') + @api.depends("product_id") def _compute_price(self): - for rec in self.filtered('product_id'): - pricing = self.env['sale.subscription.pricing']._get_first_suitable_recurring_pricing( + for rec in self.filtered("product_id"): + pricing = self.env["sale.subscription.pricing"]._get_first_suitable_recurring_pricing( rec.product_id, rec.academic_order_wizard_id.plan_id, rec.academic_order_wizard_id.pricelist_id ) rec.price = pricing.price if pricing else 0.0 diff --git a/academic_split_name/__manifest__.py b/academic_split_name/__manifest__.py index ae5f1a35..639c0b49 100644 --- a/academic_split_name/__manifest__.py +++ b/academic_split_name/__manifest__.py @@ -1,20 +1,20 @@ # © 2016 ADHOC SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { - 'name': 'Academic Split Name', - 'version': "18.0.1.0.0", - 'sequence': 14, - 'summary': '', - 'author': 'ADHOC SA', - 'website': 'www.adhoc.com.ar', - 'license': 'AGPL-3', - 'depends': [ - 'academic', + "name": "Academic Split Name", + "version": "18.0.1.0.0", + "sequence": 14, + "summary": "", + "author": "ADHOC SA", + "website": "www.adhoc.com.ar", + "license": "AGPL-3", + "depends": [ + "academic", ], - 'data': [ - 'views/res_partner_views.xml', + "data": [ + "views/res_partner_views.xml", ], - 'installable': True, - 'auto_install': False, - 'application': False, + "installable": True, + "auto_install": False, + "application": False, } diff --git a/academic_split_name/models/res_partner.py b/academic_split_name/models/res_partner.py index 8eb73c63..d3058410 100644 --- a/academic_split_name/models/res_partner.py +++ b/academic_split_name/models/res_partner.py @@ -2,7 +2,7 @@ # For copyright and license notices, see __manifest__.py file in module root # directory ############################################################################## -from odoo import fields, models, api +from odoo import api, fields, models class ResPartner(models.Model): @@ -18,8 +18,8 @@ class ResPartner(models.Model): lastname = fields.Char("Primer Apellido") second_lastname = fields.Char("Segundo Apellido") - @api.depends('firstname', 'lastname', 'second_lastname', 'middlename') + @api.depends("firstname", "lastname", "second_lastname", "middlename") def _compute_name(self): - for rec in self.filtered(lambda x: x.partner_type in ('student', 'family', 'parent')): + for rec in self.filtered(lambda x: x.partner_type in ("student", "family", "parent")): name_parts = filter(None, [rec.firstname, rec.middlename, rec.lastname, rec.second_lastname]) rec.name = " ".join(name_parts)