diff options
| -rwxr-xr-x | indoteknik_custom/__manifest__.py | 3 | ||||
| -rwxr-xr-x | indoteknik_custom/models/__init__.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/gudang_service.py | 218 | ||||
| -rwxr-xr-x | indoteknik_custom/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | indoteknik_custom/views/gudang_service.xml | 117 | ||||
| -rw-r--r-- | indoteknik_custom/views/ir_sequence.xml | 9 |
6 files changed, 349 insertions, 1 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 66962a24..ddf80cb1 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -192,7 +192,8 @@ 'views/close_tempo_mail_template.xml', 'views/domain_apo.xml', 'views/uom_uom.xml', - 'views/commission_internal.xml' + 'views/commission_internal.xml', + 'views/gudang_service.xml' ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index a14c766e..e6a59246 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -165,3 +165,4 @@ from . import partial_delivery from . import domain_apo from . import uom_uom from . import commission_internal +from . import gudang_service diff --git a/indoteknik_custom/models/gudang_service.py b/indoteknik_custom/models/gudang_service.py new file mode 100644 index 00000000..d4f7397c --- /dev/null +++ b/indoteknik_custom/models/gudang_service.py @@ -0,0 +1,218 @@ +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) + origin = fields.Many2one('sale.order', string='Origin SO', required=True) + # picking_id = fields.Many2one('stock.picking', string = 'Picking ID', domain="[('sale_id', '=', origin)]") + schedule_date = fields.Date( + string="Schedule Date", + required=True, + tracking=True + ) + + start_date = fields.Datetime( + string="Date Processed", + copy=False, + tracking=True + ) + + 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') + remaining_date = fields.Char('Unprocessed Since', compute='_compute_remaining_date') + state = fields.Selection([('draft', 'Backlog'), ('onprogress', 'On Progress'),('done', 'Done'), ('cancel', 'Cancel')], default='draft', tracking=True) + cancel_reason = fields.Text('Cancel Reason', tracking=True) + + # @api.onchange('picking_id') + # def _onchange_picking_id(self): + # if not self.picking_id: + # self.gudang_service_lines = [(5, 0, 0)] + # return + + # lines = [(5, 0, 0)] + # for move in self.picking_id.move_ids_without_package: + # if move.product_id: + # lines.append((0, 0, { + # 'product_id': move.product_id.id, + # 'quantity': move.product_uom_qty, + # 'origin_so': self.origin.id, + # })) + + # self.gudang_service_lines = lines + + + 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 + # Safa + md = self.env['res.users'].browse([3425]) + # send to logistic and safa + users = users | md + + if not users: + return + + 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 <b>%s</b> Jadwal Service 📅 <b>%s</b>' + ) % (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 <b>%s</b> Jadwal Service 📅 <b>%s</b>') % (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', '=', 'onprogress') + ]) + + if records: + records._send_logistic_notification() + + + @api.depends('start_date', 'done_date', 'state') + def _compute_remaining_date(self): + today = fields.Date.today() + + for rec in self: + if not rec.start_date: + rec.remaining_date = "-" + continue + + start = rec.start_date.date() + + if rec.state == 'done' and rec.done_date: + end = rec.done_date.date() + else: + end = today + + days = (end - start).days + rec.remaining_date = _("%s days") % days + + + + def action_submit(self): + for rec in self: + rec.state = 'onprogress' + rec.start_date = fields.Datetime.now() + # rec.date = fields.Datetime.now() + + def action_done(self): + for rec in self: + activities = self.env['mail.activity'].search([ + ('res_id', '=', rec.id), + ('res_model', '=', 'gudang.service'), + ('state', '=', 'done') + ]) + activities.unlink() + rec.state = 'done' + if not rec.done_date: + rec.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 == 'done': + 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): + # Send notification + if not vals.get('name') or vals['name'] == 'New': + vals['name'] = self.env['ir.sequence'].next_by_code('gudang.service') + + if vals.get('origin') and not vals.get('partner_id'): + so = self.env['sale.order'].browse(vals['origin']) + vals['partner_id'] = so.partner_id.id + + res = super(GudangService, self).create(vals) + # Send notification + 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') + # picking_id = fields.Many2one('stock.picking', string='Nomor Picking') + origin_so = fields.Many2one('sale.order', string='Origin SO') + gudang_service_id = fields.Many2one('gudang.service', string='Gudang Service ID')
\ No newline at end of file diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index d501de1a..84545488 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -215,3 +215,5 @@ access_surat_piutang_user,surat.piutang user,model_surat_piutang,,1,1,1,1 access_surat_piutang_line_user,surat.piutang.line user,model_surat_piutang_line,,1,1,1,1 access_sj_tele,access.sj.tele,model_sj_tele,base.group_system,1,1,1,1 access_stock_picking_sj_document,stock.picking.sj.document,model_stock_picking_sj_document,base.group_user,1,1,1,1 +access_gudang_service,gudang.service,model_gudang_service,base.group_user,1,1,1,1 +access_gudang_service_line,gudang.service.line,model_gudang_service_line,base.group_user,1,1,1,1
\ No newline at end of file diff --git a/indoteknik_custom/views/gudang_service.xml b/indoteknik_custom/views/gudang_service.xml new file mode 100644 index 00000000..42674585 --- /dev/null +++ b/indoteknik_custom/views/gudang_service.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<odoo> + <data> + <!-- Tree --> + <record id="view_gudang_service_tree" model="ir.ui.view"> + <field name="name">gudang.serivice.tree</field> + <field name="model">gudang.service</field> + <field name="arch" type="xml"> + <!-- <tree string="Monitoring Gudang Service" + decoration-warning="state == 'draft'" decoration-danger="state == 'onprogress'" + decoration-success="state == 'done'" decoration-muted="state == 'cancel'" + decoration-bf="remaining_date > 7" + > --> + <tree string="Monitoring Barang Service" + decoration-danger="state == 'draft'" decoration-warning="state == 'onprogress'" + decoration-success="state == 'done'" decoration-muted="state == 'cancel'" + > + <field name="name"/> + <field name="partner_id"/> + <field name="origin"/> + <field name="schedule_date"/> + <field name="start_date" optional="hide"/> + <field name="remaining_date"/> + <field name="state" widget="badge" decoration-danger="state in ('draft')" decoration-warning="state == 'onprogress'" + decoration-success="state == 'done'" decoration-muted="state == 'cancel'" /> + <field name="cancel_reason" optional="hide"/> + <!-- <field name="picking_id"/> --> + </tree> + </field> + </record> + <!-- Form --> + <record id="view_gudang_service_form" model="ir.ui.view"> + <field name="name">gudang.service.form</field> + <field name="model">gudang.service</field> + <field name="arch" type="xml"> + <form> + <header> + <button name="action_submit" string="Proceed" type="object" + class="btn-primary" + attrs="{'invisible': [('state', 'in', ['cancel', 'done', 'onprogress'])]}"/> + <button name="action_done" string="Set Done" type="object" + class="btn-primary" + attrs="{'invisible': [('state', 'not in', ['onprogress'])]}"/> + <button name="action_cancel" string="Cancel" type="object" + class="btn-secondary" + attrs="{'invisible': [('state', 'in', ['cancel', 'done', 'draft'])]}"/> + <button name="action_draft" string="Set to Backlog" type="object" + class="btn-secondary" + attrs="{'invisible': [('state', 'in', ['draft', 'done', 'onprogress'])]}"/> + <field name="state" widget="statusbar" readonly="1"/> + </header> + <sheet> + <div class="oe_title"> + <h1> + <field name="name" readonly="1" class="oe_inline"/> + </h1> + </div> + <group> + <field name="origin" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/> + <field name="partner_id"/> + <field name="remaining_date"/> + <field name="schedule_date" attrs="{'readonly': [('state', 'not in', ['draft'])]}"/> + <field name="start_date" readonly="1"/> + <field name="done_date" attrs="{'invisible': [('state', 'not in', ['done'])]}"/> + <field name="create_uid"/> + <field name="cancel_reason" + attrs="{'invisible': [('state', 'in', ['done', 'draft'])]}"/> + </group> + <notebook> + <page string="Product Lines" name="product_lines"> + <field name="gudang_service_lines"> + <tree string="Product Lines" editable="top" create="0" delete="1"> + <field name="product_id"/> + <!-- <field name="quantity"/> --> + <!-- <field name="picking_id"/> --> + </tree> + </field> + </page> + </notebook> + </sheet> + <div class="oe_chatter"> + <field name="message_follower_ids" widget="mail_followers"/> + <field name="message_ids" widget="mail_thread"/> + </div> + </form> + </field> + </record> + <!-- Action --> + <record id="action_gudang_service" model="ir.actions.act_window"> + <field name="name">Monitoring Barang Service</field> + <field name="type">ir.actions.act_window</field> + <field name="res_model">gudang.service</field> + <field name="view_mode">tree,form</field> + <!-- <field name="context">{'group_by': ['state']}</field> --> + </record> + + <!-- Menu --> + <menuitem + id="menu_gudang_service" + name="Monitoring Barang Service" + parent="indoteknik_custom.menu_monitoring_in_sale" + sequence="10" + action="action_gudang_service" + /> + </data> + <!-- Cron --> + <record id="ir_cron_gudang_service_logistik_notify" model="ir.cron"> + <field name="name">Gudang Service Daily Notification</field> + <field name="model_id" ref="model_gudang_service"/> + <field name="state">code</field> + <field name="code">model.cron_notify_onprogress_gudang_service()</field> + <field name="interval_number">1</field> + <field name="interval_type">days</field> + <field name="numbercall">-1</field> + <field name="active">False</field> + </record> +</odoo> diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml index 46148606..55e48300 100644 --- a/indoteknik_custom/views/ir_sequence.xml +++ b/indoteknik_custom/views/ir_sequence.xml @@ -260,5 +260,14 @@ <field name="number_next">1</field> <field name="number_increment">1</field> </record> + + <record id="seq_gudang_service" model="ir.sequence"> + <field name="name">Gudang Service</field> + <field name="code">gudang.service</field> + <field name="prefix">MGS/%(year)s/%(month)s/</field> + <field name="padding">4</field> + <field name="number_next">1</field> + <field name="number_increment">1</field> + </record> </data> </odoo>
\ No newline at end of file |
