from asyncore import write from odoo import fields, models, api, _ from odoo.exceptions import UserError, ValidationError from datetime import timedelta from dateutil.relativedelta import relativedelta class SeasonalService(models.Model): _name = 'seasonal.service' _inherit = ['mail.thread', 'mail.activity.mixin'] _order = "name desc" name = fields.Char(string='Reference', required=True, copy=False, readonly=True, index=True, default=lambda self: _('New')) date = fields.Datetime(string='Request Date', default=fields.Datetime.now, copy=False) service_type_id = fields.Many2one('services.settings', domain="[('is_seasonal_service','=',True)]", string="Seasonal Service Type", required=True) benefit_type = fields.Selection(string='Benefit Type', related='service_type_id.benefit_type') branch_ids = fields.Many2many('branch.settings', 'service_branch_rel', 'service_id', 'branch_id', string='Branches', required=True, domain="[('has_employees', '=', True)]") allowed_service_categories = fields.Many2many( comodel_name='benefit.category', related='service_type_id.benefit_category_ids', string="Allowed Categories", readonly=True ) family_category_ids = fields.Many2many( 'benefit.category', 'service_category_rel', 'service_id', 'category_id', string='Family Category', domain="[('id', 'in', allowed_service_categories)]") aid_amount = fields.Float(string="Aid Amount", compute="_compute_amounts", store=True, readonly=False) benefit_member_count = fields.Integer(string="Benefit Member count", compute="compute_family_benefit", store=True) family_count = fields.Integer(string="Family count", compute="compute_family_benefit", store=True) family_disbursement_total_amount = fields.Float(string="Total Family Disbursement Amount", compute="_compute_family_disbursement", store=True) payment_order_id = fields.Many2one('payment.orders', string='Payment Order', copy=False, ondelete="set null") payment_order_count = fields.Integer(compute='_compute_payment_order', string='Number of Payment Orders') vendor_bill = fields.Many2one('account.move', copy=False) total_moves = fields.Integer(string="Total Move", compute='_get_total_move_lines') state = fields.Selection(selection=[ ('draft', 'Draft'), ('calculated', 'Calculated'), ('gm_assistant', 'Waiting Assistant General Manager'), ('accounting_approve', 'Accounting Approve'), ('waiting_receive', 'Waiting for Receive'), ('done', 'Done'), ], string='state', default='draft', tracking=True, copy=False) is_payment_order_done = fields.Boolean(string='Is Payment Order Done?', copy=False) payment_order_state = fields.Selection(string='Payment Order State', selection=[ ('none', 'None'), ('waiting', 'Waiting Payment'), ('done', 'Done Payment'), ], copy=False, compute="_compute_payment_order_state", store=True) account_id = fields.Many2one('account.account', string='Expenses Account', related="service_type_id.account_id") service_requests_ids = fields.One2many('service.request', 'seasonal_service_id', string='Service Requests') benefit_ids = fields.Many2many(comodel_name='grant.benefit', relation='benefit_grant_seasonal_service_rel', column1='seasonal_service_id', column2='benefit_id', string='Families', copy=False) member_ids = fields.Many2many(comodel_name='family.member', relation='family_member_seasonal_service_rel', column1='seasonal_service_id', column2='family_member_id', string='Family Members', copy=False) family_domain_ids = fields.Many2many(comodel_name='grant.benefit', compute='_compute_domain_ids', string="Eligible Families") allowed_member_ids = fields.Many2many('family.member', compute='_compute_allowed_member_ids', string="Allowed Members", ) service_delivery_method = fields.Selection(selection=[ ('cash', 'Cash'), ('in_kind', 'In kind'), ], string='Service Delivery Method', default='cash') is_in_kind = fields.Boolean(string="In Kind", related="service_type_id.in_kind") company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.user.company_id) currency_id = fields.Many2one('res.currency', string="Currency", related='company_id.currency_id') @api.depends('payment_order_id', 'payment_order_id.state', 'vendor_bill', 'vendor_bill.state') def _compute_payment_order_state(self): for rec in self: payment_order_state = 'none' if rec.payment_order_id: if rec.payment_order_id.state == "done": payment_order_state = "done" rec.state = 'waiting_receive' else: payment_order_state = "waiting" elif rec.vendor_bill: if rec.vendor_bill.state == "posted": payment_order_state = "done" rec.state = 'waiting_receive' else: payment_order_state = "waiting" rec.payment_order_state = payment_order_state @api.depends('payment_order_id') def _compute_payment_order(self): for rec in self: if rec.payment_order_id: rec.payment_order_count = 1 else: rec.payment_order_count = 0 @api.depends('vendor_bill') def _get_total_move_lines(self): for rec in self: if rec.vendor_bill: moves = 1 else: moves = 0 rec.total_moves = moves @api.depends('benefit_ids', 'service_type_id') def _compute_allowed_member_ids(self): for rec in self: domain = [('benefit_id', 'in', rec.benefit_ids.ids), ('member_status', '=', 'benefit')] if rec.service_type_id.max_age: domain.append(('age', '<=', rec.service_type_id.max_age)) members = self.env['family.member'].sudo().search(domain) if rec.service_type_id.is_this_service_for_student: members = members.filtered( lambda m: ( m.education_status == 'educated' and any(edu.case_study == 'continuous' for edu in m.member_education_status_ids) ) ) rec.allowed_member_ids = members @api.onchange('service_type_id') def _onchange_service_type_id(self): if self.benefit_ids: self.benefit_ids = [(5, 0, 0)] self.family_category_ids = [(5, 0, 0)] if self.service_type_id and self.service_type_id.benefit_category_ids: self.family_category_ids = [(6, 0, self.service_type_id.benefit_category_ids.ids)] @api.depends('branch_ids', 'family_category_ids') def _compute_domain_ids(self): for rec in self: domain = ['|', ('state', '=', 'second_approve'), '&', ('state', 'in', ('waiting_approve', 'first_approve')), ('action_type', '=', 'suspended')] if self.branch_ids: domain.append(('branch_custom_id', 'in', self.branch_ids.ids)) if self.family_category_ids: domain.append(('benefit_category_id', 'in', self.family_category_ids.ids)) families = self.env['grant.benefit'].sudo().search(domain) rec.family_domain_ids = families @api.onchange('branch_ids', 'family_category_ids') def _onchange_cleanup_families_members(self): if not self.branch_ids or not self.family_category_ids: self.benefit_ids = [(5, 0, 0)] return allowed_branch_ids = set(self.branch_ids.ids) allowed_category_ids = set(self.family_category_ids.ids) families_to_keep = self.benefit_ids.filtered( lambda b: bool(b.branch_custom_id and b.branch_custom_id.id in allowed_branch_ids) and bool(b.benefit_category_id and b.benefit_category_id.id in allowed_category_ids) ) # apply families kept (replace whole m2m) if len(families_to_keep) != len(self.benefit_ids): self.benefit_ids = [(6, 0, families_to_keep.ids)] @api.onchange('benefit_ids') def _onchange_benefit_ids_cleanup_members(self): if not self.benefit_ids: self.member_ids = [(5, 0, 0)] return current_family_ids = set(self.benefit_ids.ids) members_to_keep = self.member_ids.filtered( lambda m: m.benefit_id and m.benefit_id.id in current_family_ids ) if len(members_to_keep) != len(self.member_ids): self.member_ids = [(6, 0, members_to_keep.ids)] @api.model def create(self, vals): res = super(SeasonalService, self).create(vals) if not res.name or res.name == _('New'): res.name = self.env['ir.sequence'].sudo().next_by_code('seasonal.service.sequence') or _('New') return res def unlink(self): for service in self: if service.state not in ['draft']: raise UserError(_('You cannot delete this record')) return super(SeasonalService, self).unlink() def action_create_payment_order(self): for rec in self: if rec.state != 'accounting_approve': raise UserError(_("All selected requests should be in Accounting Approve state")) if rec.payment_order_id: raise UserError(_("There are already disbursement orders associated with the request(s).")) self.env['payment.orders'].create({ 'state': 'draft', 'accountant_id': rec.service_type_id.accountant_id.id, 'seasonal_requests_ids': rec.ids, 'service_requests_ids': rec.service_requests_ids.ids, 'is_seasonal': True, 'type': 'seasonal_services', 'payment_order_description': rec.service_type_id.id, }) # rec.payment_order_id = payment_order.id rec.is_payment_order_done = True def action_accounting_transfer(self): validation_setting = self.env["family.validation.setting"].search([], limit=1) line_ids = [] for rec in self: if rec.service_type_id.payment_method == "payment_order": if rec.payment_order_id: raise UserError(_( "A payment order is already linked to this request: %s" ) % rec.name) payment_order = self.env['payment.orders'].create({ 'state': 'draft', 'accountant_id': rec.service_type_id.accountant_id.id, 'seasonal_requests_ids': rec.ids, 'service_requests_ids': rec.service_requests_ids.ids, 'is_seasonal': True, 'type': 'seasonal_services', 'payment_order_description':rec.service_type_id.id, }) rec.payment_order_id = payment_order.id elif rec.service_type_id.payment_method == "invoice": if rec.vendor_bill: raise UserError(_( "A vendor bill is already linked to this request: %s" ) % rec.name) if not rec.service_requests_ids: raise UserError(_( "No service requests found to generate a vendor bill for: %s" ) % rec.name) for request in rec.service_requests_ids: invoice_line = (0, 0, { 'name': f'{request.family_id.name}/{request.description}-{request.name}', 'benefit_family_id': request.family_id.id, 'account_id': request.account_id.id, 'analytic_account_id': request.family_id.branch_family_id.branch.analytic_account_id.id, 'quantity': request.requested_quantity or 1, # todo check 'price_unit': request.requested_service_amount, }) line_ids.append(invoice_line) vendor_bill = self.env['account.move'].sudo().create({ 'move_type': 'in_invoice', 'partner_id': rec.service_type_id.service_producer_id.id, 'journal_id': validation_setting.journal_id.id, 'invoice_line_ids': line_ids, }) rec.vendor_bill = vendor_bill @api.depends('service_requests_ids') def compute_family_benefit(self): for record in self: families = record.service_requests_ids.mapped('family_id') family_count = len(families) member_count = sum(record.service_requests_ids.mapped('service_benefit_count')) record.family_count = family_count record.benefit_member_count = member_count @api.depends('service_type_id') def _compute_amounts(self): for record in self: if record.service_type_id and record.service_delivery_method == 'cash': record.aid_amount = record.service_type_id.max_amount else: record.aid_amount = 0 @api.depends('service_requests_ids') def _compute_family_disbursement(self): for record in self: if record.service_requests_ids: if record.service_delivery_method == "cash": record.family_disbursement_total_amount = sum( record.service_requests_ids.mapped('requested_service_amount')) else: record.family_disbursement_total_amount = sum( record.service_requests_ids.mapped('service_qty')) else: record.family_disbursement_total_amount = 0.0 def action_first_approval(self): for rec in self: if not rec.service_requests_ids: raise UserError(_("You must add at least one service request.")) if rec.service_type_id.needs_beneficiary_manager_approval: rec.state = 'gm_assistant' rec.service_requests_ids.write({'state': 'gm_assistant'}) elif rec.service_delivery_method == "cash": rec.action_accounting_transfer() rec.state = "accounting_approve" rec.service_requests_ids.write({'state': 'accounting_approve'}) elif rec.service_delivery_method == "in_kind": rec.state = 'waiting_receive' rec.service_requests_ids.write({'state': 'send_request_to_supplier'}) def action_approval_of_gm_assistant(self): for rec in self: if rec.service_delivery_method == "cash": rec.action_accounting_transfer() rec.state = "accounting_approve" rec.service_requests_ids.write({'state': 'accounting_approve'}) elif rec.service_delivery_method == "in_kind": rec.state = 'waiting_receive' rec.service_requests_ids.write({'state': 'send_request_to_supplier'}) def action_done(self): for rec in self: rec.state = 'done' rec.service_requests_ids.write({'state': 'family_received_device'}) def action_calculate(self): for rec in self: rec._generate_service_requests() rec.state = 'calculated' def action_recalculate(self): for rec in self: rec.service_requests_ids.unlink() rec._generate_service_requests() def _generate_service_requests(self): Service = self.env['service.request'] for rec in self: if not rec.benefit_ids: raise UserError(_("You must add at least one family.")) if rec.benefit_type == "family": for benefit in rec.benefit_ids: service_request = Service.create({ 'family_id': benefit.id, 'service_cat': rec.service_type_id.id, 'seasonal_service_id': rec.id, 'benefit_type': rec.benefit_type, }) service_request.onchange_requested_service_amount() if rec.service_delivery_method == "cash": service_request.requested_service_amount = service_request.service_max_amount if service_request.requested_service_amount == 0: service_request.unlink() else: service_request.requested_service_amount = 0 service_request.is_in_kind = True service_request.service_qty = rec.aid_amount else: if not rec.member_ids: raise UserError(_("You must add at least one member.")) for member in rec.member_ids: if not member.benefit_id: raise UserError(_("Member %s has no related family (benefit).") % member.name) service_request = Service.create({ 'family_id': member.benefit_id.id, 'member_id': member.id, 'service_cat': rec.service_type_id.id, 'seasonal_service_id': rec.id, 'benefit_type': rec.benefit_type, }) service_request.onchange_requested_service_amount() if rec.service_delivery_method == "cash": service_request.requested_service_amount = service_request.service_max_amount if service_request.requested_service_amount == 0: service_request.unlink() else: service_request.requested_service_amount = 0 service_request.is_in_kind = True service_request.service_qty = rec.aid_amount def action_reset_to_draft(self): for rec in self: rec.service_requests_ids.write({'state': 'draft'}) rec.service_requests_ids.unlink() rec.state = 'draft' def action_reset_to_calculated(self): self.ensure_one() return { 'name': _('Reason for Return'), 'type': 'ir.actions.act_window', 'res_model': 'reason.for.return.wizard', 'view_mode': 'form', 'target': 'new', } def action_open_related_move_records(self): return { 'name': _('Vendor Bills'), 'type': 'ir.actions.act_window', 'res_model': 'account.move', 'view_mode': 'tree,form', 'domain': [('id', 'in', self.vendor_bill.ids)], } def action_open_payment_orders(self): self.ensure_one() if not self.payment_order_id: raise UserError(_("No payment order are linked to this request.")) return { 'name': _('Payment Orders'), 'type': 'ir.actions.act_window', 'res_model': 'payment.orders', 'view_mode': 'tree,form', 'domain': [('id', 'in', self.payment_order_id.ids)], 'context': {'create': False}, }