From 99990bdfcc87025e3b4f4492709e29a19f7512fa Mon Sep 17 00:00:00 2001 From: Luis Ernesto Portillo Zaldivar Date: Tue, 15 Jul 2025 11:56:50 -0600 Subject: [PATCH] =?UTF-8?q?feat(#51):=20Task=204=20completada=20-=20Agrega?= =?UTF-8?q?r=20m=C3=A9todo=20=5Fcompute=5Fage()=20en=20res.partner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregado campo computado 'age' que calcula edad en años desde birthdate_date - Agregado campo 'is_pregnant' con validación de género femenino - Implementado método _compute_age() usando relativedelta para precisión - Agregado método helper get_age_at_date() para cálculos en fechas específicas - Actualizada vista de pacientes para mostrar edad y estado de embarazo - Validación que previene marcar embarazo en género masculino 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../__pycache__/partner.cpython-312.pyc | Bin 2194 -> 4720 bytes lims_management/models/partner.py | 57 ++++++++++++++++++ lims_management/views/partner_views.xml | 4 ++ 3 files changed, 61 insertions(+) diff --git a/lims_management/models/__pycache__/partner.cpython-312.pyc b/lims_management/models/__pycache__/partner.cpython-312.pyc index cb379f77e55b67a891bf8f27276ba019eefccb04..0ba6f1010efbb703b8c95701f5f996ea7468b67b 100644 GIT binary patch literal 4720 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 delta 606 zcmYLG&ubGw6rLZuo3u?f*(OZ_Nrl=7u7_R(Z9%1|AShb3cv+C`x--pgI-8W)E&e$7 z;L$ij{{unNn|Dv%6r=|c<{)?yJrz?go}5WqI){(%&G+8>-n=<%zjo|T#o{zj=gZqH zdTb8t#`)_i+5-d-$Up);m>>@&*uxMYa0?K$3kasLn->d($_1=z0r1S{hHL)P)WyDnTAMLtB$YVWj>Ca7hc@8g&yPoAKb?Adh`o=8a1whq0@Uc*r|JnKC1UPdLv);1 zB4y%qkXNQow=KS-dVO{`KRizMDlfq^=vCK&TM<>f2J?d8ZG~U)mZ+M)1uWFwThq(1 zno||h3ONlj>Kf8WDcckeX3mKycd0S2#23y(n%)n1)}wrBk~c5j%tX17_HyUMozm6S zy6$4!hyuV>WHvkJL>#25@Md;}sXKZOO+be0#VzM0Jf zvpGVBxiZ4QY8`3D7f&kgxt93g)YinQB&=fcBAb2ClrS?X?E2zd+G(@qfJUkB|TW diff --git a/lims_management/models/partner.py b/lims_management/models/partner.py index f90bc73..b2f567d 100644 --- a/lims_management/models/partner.py +++ b/lims_management/models/partner.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- from odoo import models, fields, api +from odoo.exceptions import ValidationError +from datetime import date +from dateutil.relativedelta import relativedelta class ResPartner(models.Model): _inherit = 'res.partner' @@ -17,6 +20,19 @@ class ResPartner(models.Model): ('female', 'Femenino'), ('other', 'Otro') ], string="Género") + + # Nuevos campos para el cálculo de rangos + age = fields.Integer( + string="Edad", + compute='_compute_age', + store=False, + help="Edad calculada en años basada en la fecha de nacimiento" + ) + + is_pregnant = fields.Boolean( + string="Embarazada", + help="Marcar si la paciente está embarazada (solo aplica para género femenino)" + ) is_doctor = fields.Boolean(string="Es Médico") doctor_license = fields.Char(string="Licencia Médica", copy=False) @@ -25,6 +41,25 @@ class ResPartner(models.Model): ('patient_identifier_unique', 'unique(patient_identifier)', 'El identificador del paciente debe ser único.'), ('doctor_license_unique', 'unique(doctor_license)', 'La licencia médica debe ser única.') ] + + @api.depends('birthdate_date') + def _compute_age(self): + """Calcula la edad en años basada en la fecha de nacimiento""" + today = date.today() + for partner in self: + if partner.birthdate_date: + # Calcular diferencia usando relativedelta para precisión + delta = relativedelta(today, partner.birthdate_date) + partner.age = delta.years + else: + partner.age = 0 + + @api.constrains('is_pregnant', 'gender') + def _check_pregnant_gender(self): + """Valida que solo pacientes de género femenino puedan estar embarazadas""" + for partner in self: + if partner.is_pregnant and partner.gender != 'female': + raise ValidationError('Solo las pacientes de género femenino pueden estar marcadas como embarazadas.') @api.model_create_multi def create(self, vals_list): @@ -32,3 +67,25 @@ class ResPartner(models.Model): if vals.get('is_patient') and not vals.get('patient_identifier'): vals['patient_identifier'] = self.env['ir.sequence'].next_by_code('res.partner.patient_identifier') return super(ResPartner, self).create(vals_list) + + def get_age_at_date(self, target_date=None): + """ + Calcula la edad del paciente en una fecha específica. + + :param target_date: Fecha en la que calcular la edad. Si es None, usa la fecha actual. + :return: Edad en años + """ + self.ensure_one() + if not self.birthdate_date: + return 0 + + if not target_date: + target_date = date.today() + elif isinstance(target_date, str): + target_date = fields.Date.from_string(target_date) + + if target_date < self.birthdate_date: + return 0 + + delta = relativedelta(target_date, self.birthdate_date) + return delta.years diff --git a/lims_management/views/partner_views.xml b/lims_management/views/partner_views.xml index 62aa9c4..dc2be18 100644 --- a/lims_management/views/partner_views.xml +++ b/lims_management/views/partner_views.xml @@ -11,6 +11,8 @@ + + @@ -43,7 +45,9 @@ + +