from odoo import models, fields, api, _ from odoo.exceptions import UserError, ValidationError import logging from datetime import datetime from collections import defaultdict class GudangService(models.Model): _name = "gudang.service" _description = "Gudang Service" _inherit = ['mail.thread', 'mail.activity.mixin'] _order = 'id asc' name = fields.Char('Name', readonly=True) partner_id = fields.Many2one('res.partner', string='Customer', readonly=True) vendor_id = fields.Many2one('res.partner', string='Vendor Service', required=True) origin = fields.Many2one('sale.order', string='Origin SO', required=True, domain=[('state', 'in', ['done', 'sale'])]) schedule_date = fields.Date( string="Schedule Date", required=True, tracking=True ) start_date = fields.Datetime( string="Date Processed", copy=False, tracking=True ) create_date = fields.Datetime(string='Create Date', copy=False, tracking=True, default=fields.Datetime.now()) done_date = fields.Datetime(string='Date Done', copy=False, tracking=True) gudang_service_lines = fields.One2many('gudang.service.line', 'gudang_service_id', string='Gudang Service Lines') # unprocessed_date = fields.Char( # string='Unprocessed Since', # compute='_compute_unprocessed_date' # ) remaining_date = fields.Char( compute='_compute_remaining_date', string='Date Status' ) state = fields.Selection([ ('draft', 'Backlog'), ('received_from_cust', 'Received From Customer'), ('sent_to_vendor', 'Sent to Service Vendor'), ('received_from_vendor', 'Received From Service Vendor'), ('delivered_to_cust', 'Delivered to Customer'), ('cancel', 'Cancel')], default='draft', tracking=True) cancel_reason = fields.Text('Cancel Reason', tracking=True) @api.constrains('gudang_service_lines') def _check_qty(self): for rec in self: if not rec.origin: continue so_qty_map = { line.product_id.id: line.product_uom_qty for line in rec.origin.order_line } for line in rec.gudang_service_lines: if line.quantity <= 0: raise ValidationError( f"Quantity for product {line.product_id.display_name} cannot be 0 or negative." ) so_qty = so_qty_map.get(line.product_id.id, 0) if line.quantity > so_qty: raise ValidationError( f"Quantity for product {line.product_id.display_name} " f"cannot exceed SO quantity ({so_qty})." ) @api.constrains('state', 'schedule_date') def _check_edit_after_sent(self): for rec in self: if rec.state in ['sent_to_vendor', 'received_from_vendor', 'delivered_to_cust']: if rec._origin.schedule_date != rec.schedule_date: raise ValidationError("Schedule cannot be modified after sent to vendor") def _send_logistic_notification(self): group = self.env.ref('indoteknik_custom.group_role_logistic', raise_if_not_found=False) if not group: return users = group.users # MD md = self.env['res.users'].browse([3425, 4801, 1036]) # send to logistic and MD users = users | md if not users: return # Logistic users to be excluded excluded_users = [7, 17098, 216, 28, 15710] for rec in self: for user in users: if user.id in excluded_users: continue self.env['mail.activity'].create({ 'res_model_id': self.env['ir.model']._get_id('gudang.service'), 'res_id': rec.id, 'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id, 'user_id': user.id, 'summary': 'Gudang Service On Progress', 'note': _( 'Ada Jadwal Service Barang di Document %s Jadwal Service 📅 %s' ) % (rec.name, rec.schedule_date), # 'date_deadline': fields.Date.today(), }) # kirim ke private message odoo channel = self.env['mail.channel'].channel_get([self.env.user.partner_id.id, user.partner_id.id]).get('id') if not channel: continue res = self.env['mail.channel'].browse(channel) res.with_user(self.env.user.browse(25)).message_post(body=_('Ada Jadwal Service Barang di Document %s Jadwal Service 📅 %s') % (rec.name, rec.schedule_date), message_type='comment', subtype_xmlid='mail.mt_comment') @api.model def cron_notify_onprogress_gudang_service(self): records = self.search([ ('state', 'in', ['draft', 'received_from_cust']), ]) if records: records._send_logistic_notification() @api.depends('schedule_date', 'create_date') def _compute_remaining_date(self): today = fields.Date.today() for rec in self: if not rec.schedule_date: rec.remaining_date = "-" continue base_date = rec.create_date.date() if rec.create_date else today schedule = rec.schedule_date days = (schedule - base_date).days if days > 0: rec.remaining_date = _("In %s days") % days elif days == 0: rec.remaining_date = _("Today") else: rec.remaining_date = _("Overdue %s days") % abs(days) def action_submit(self): for rec in self: if rec.state == 'draft': rec.state = 'received_from_cust' elif rec.state == 'received_from_cust': rec.state = 'sent_to_vendor' rec.start_date = fields.Datetime.now() elif rec.state == 'sent_to_vendor': rec.state = 'received_from_vendor' def action_done(self): for rec in self: if rec.state != 'received_from_vendor': raise UserError("Only 'Received From Vendor' state can be set to Done") rec.activity_ids.unlink() rec.write({ 'state': 'delivered_to_cust', 'done_date': fields.Datetime.now() }) def action_draft(self): """Reset to draft state""" for rec in self: rec.cancel_reason = False if rec.state == 'cancel': rec.write({'state': 'draft'}) else: raise UserError("Only Canceled Record Can Be Reset To Draft") def action_cancel(self): for rec in self: activities = self.env['mail.activity'].search([ ('res_id', '=', rec.id), ('res_model', '=', 'gudang.service'), ]) activities.unlink() if rec.state == 'delivered_to_cust': raise UserError("You cannot cancel a done record") if not rec.cancel_reason: raise UserError("Cancel Reason must be filled") rec.start_date = False rec.done_date = False rec.state = 'cancel' @api.model def create(self, vals): # sequence if not vals.get('name') or vals['name'] == 'New': vals['name'] = self.env['ir.sequence'].next_by_code('gudang.service') # partner dari SO so = self.env['sale.order'].browse(vals['origin']) vals['partner_id'] = so.partner_id.id res = super(GudangService, self).create(vals) res._send_logistic_notification() return res def write(self, vals): if vals.get('origin'): so = self.env['sale.order'].browse(vals['origin']) vals['partner_id'] = so.partner_id.id return super(GudangService, self).write(vals) @api.onchange('origin') def _onchange_origin(self): if not self.origin: self.gudang_service_lines = [(5, 0, 0)] return self.partner_id = self.origin.partner_id lines = [] for line in self.origin.order_line: lines.append((0, 0, { 'product_id': line.product_id.id, 'quantity': line.product_uom_qty, 'origin_so': self.origin.id, })) # hapus line lama lalu isi baru self.gudang_service_lines = [(5, 0, 0)] + lines class GudangServiceLine(models.Model): _name = "gudang.service.line" _inherit = ['mail.thread', 'mail.activity.mixin'] product_id = fields.Many2one('product.product', string='Product') quantity = fields.Float(string='Quantity') origin_so = fields.Many2one('sale.order', string='Origin SO') gudang_service_id = fields.Many2one('gudang.service', string='Gudang Service ID')