diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2024-03-12 04:56:55 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2024-03-12 04:56:55 +0000 |
| commit | f43c76e2e7fb1a2e46f6e698afb1da74961cad50 (patch) | |
| tree | 18b80d3d6dd74022f01d0663d4a349be6c662193 | |
| parent | 41056a3fcf9cf80ac3609ab32223ffbac5b3ad83 (diff) | |
| parent | b7b71be97a73f454f2df9fd9a37f5017c82192ae (diff) | |
Merged in feature/web-sale-approval (pull request #136)
Feature/web sale approval
| -rw-r--r-- | indoteknik_api/controllers/api_v1/sale_order.py | 74 | ||||
| -rw-r--r-- | indoteknik_api/models/__init__.py | 1 | ||||
| -rw-r--r-- | indoteknik_api/models/res_partner.py | 14 | ||||
| -rw-r--r-- | indoteknik_api/models/res_users.py | 29 | ||||
| -rw-r--r-- | indoteknik_api/models/sale_order.py | 7 | ||||
| -rw-r--r-- | indoteknik_custom/models/res_partner.py | 15 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 33 | ||||
| -rw-r--r-- | indoteknik_custom/views/res_partner.xml | 4 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 45 |
9 files changed, 205 insertions, 17 deletions
diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 62f20ed8..53a9b127 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -79,12 +79,14 @@ class SaleOrder(controller.Controller): ('name', 'ilike', '%' + name + '%'), ('partner_purchase_order_name', 'ilike', '%' + name + '%') ] + sale_orders = request.env['sale.order'].search( domain, offset=offset, limit=limit) data = { 'sale_order_total': request.env['sale.order'].search_count(domain), 'sale_orders': [request.env['sale.order'].api_v1_single_response(x) for x in sale_orders] } + return self.response(data) @http.route(PREFIX_PARTNER + 'sale_order/<id>', auth='public', method=['GET', 'OPTIONS']) @@ -102,8 +104,7 @@ class SaleOrder(controller.Controller): data = {} sale_order = request.env['sale.order'].search(domain) if sale_order: - data = request.env['sale.order'].api_v1_single_response( - sale_order, context='with_detail') + data = request.env['sale.order'].api_v1_single_response(sale_order, context='with_detail') return self.response(data) @@ -117,8 +118,7 @@ class SaleOrder(controller.Controller): if not params['valid']: return self.response(code=400, description=params) - partner_child_ids = self.get_partner_child_ids( - params['value']['partner_id']) + partner_child_ids = self.get_partner_child_ids(params['value']['partner_id']) domain = [ ('id', '=', params['value']['id']), ('partner_id', 'in', partner_child_ids) @@ -131,6 +131,72 @@ class SaleOrder(controller.Controller): sale_order, context='with_detail') return self.response(data) + + @http.route(PREFIX_PARTNER + 'sale_order/<id>/approve', auth='public', method=['POST', 'OPTIONS'], csrf=False) + @controller.Controller.must_authorized(private=True, private_key='partner_id') + def partner_approve_sale_order_by_id(self, **kw): + params = self.get_request_params(kw, { + 'partner_id': ['number'], + 'id': ['number'] + }) + + if not params['valid']: + return self.response(code=400, description=params) + + value = params['value'] + partner_child_ids = self.get_partner_child_ids(value['partner_id']) + + sale_order = request.env['sale.order'].search([ + ('id', '=', value['id']), + ('partner_id', 'in', partner_child_ids) + ]) + if not sale_order: + return self.response(code=404, description='Sale Order not found') + + partner = request.env['res.partner'].browse(value['partner_id']) + if not partner.web_role: + return self.response(code=400, description='Unauthorized') + + if partner.web_role: + sale_order.web_approval = 'cust_%s' % partner.web_role + + if sale_order.web_approval == 'cust_director': + sale_order.approval_status = 'pengajuan1' + + return self.response('success') + + @http.route(PREFIX_PARTNER + 'sale_order/<id>/reject', auth='public', method=['POST', 'OPTIONS'], csrf=False) + @controller.Controller.must_authorized(private=True, private_key='partner_id') + def partner_reject_sale_order_by_id(self, **kw): + params = self.get_request_params(kw, { + 'partner_id': ['number'], + 'id': ['number'] + }) + + if not params['valid']: + return self.response(code=400, description=params) + + value = params['value'] + partner_child_ids = self.get_partner_child_ids(value['partner_id']) + + sale_order = request.env['sale.order'].search([ + ('id', '=', value['id']), + ('partner_id', 'in', partner_child_ids) + ]) + if not sale_order: + return self.response(code=404, description='Sale Order not found') + + partner = request.env['res.partner'].browse(value['partner_id']) + if not partner.web_role: + return self.response(code=400, description='Unauthorized') + + if partner.web_role: + sale_order.web_approval = 'cust_%s' % partner.web_role + + sale_order.state = 'cancel' + sale_order.approval_status = False + + return self.response('success') @http.route(PREFIX_PARTNER + 'sale_order/<id>/upload_po', auth='public', method=['POST', 'OPTIONS'], csrf=False) def partner_upload_po_sale_order(self, **kw): diff --git a/indoteknik_api/models/__init__.py b/indoteknik_api/models/__init__.py index 892d2657..8c85938c 100644 --- a/indoteknik_api/models/__init__.py +++ b/indoteknik_api/models/__init__.py @@ -9,3 +9,4 @@ from . import sale_order from . import x_manufactures from . import website_content from . import coupon_program +from . import res_partner diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py new file mode 100644 index 00000000..57200ac1 --- /dev/null +++ b/indoteknik_api/models/res_partner.py @@ -0,0 +1,14 @@ +from odoo import models + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + def get_main_parent(self): + partner = self + + while partner.parent_id: + partner = partner.parent_id + + return partner +
\ No newline at end of file diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py index f331321f..456e52d7 100644 --- a/indoteknik_api/models/res_users.py +++ b/indoteknik_api/models/res_users.py @@ -6,27 +6,32 @@ class ResUsers(models.Model): def api_single_response(self, res_user, with_detail=''): config = self.env['ir.config_parameter'] - - user_pricelist = res_user.property_product_pricelist - pricelist_tier = user_pricelist.sudo().get_tier_name() + + partner = res_user.partner_id + main_partner = partner.get_main_parent() + + WEB_ROLE_ALIAS = { + 'manager': 2, + 'director': 3 + } data = { 'id': res_user.id, - 'parent_id': res_user.parent_id.id or False, - 'parent_name': res_user.parent_id.name or False, - 'partner_id': res_user.partner_id.id, + 'parent_id': res_user.parent_id.id or None, + 'parent_name': res_user.parent_id.name or None, + 'partner_id': partner.id, 'name': res_user.name, 'email': res_user.login, 'phone': res_user.phone or '', 'mobile': res_user.mobile or '', 'external': res_user.share, - 'company': res_user.company_type == 'company', - 'pricelist': pricelist_tier + 'company': main_partner.company_type == 'company', + 'pricelist': res_user.property_product_pricelist.sudo().get_tier_name(), + 'web_role': WEB_ROLE_ALIAS[partner.web_role] if partner.web_role else None, + 'feature': { + 'so_approval': main_partner.use_so_approval + } } - - - if res_user.parent_id: - data.update({ 'company': res_user.parent_id.company_type == 'company' }) return data diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 85bf5015..1c0180ec 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -5,6 +5,12 @@ class SaleOrder(models.Model): _inherit = 'sale.order' def api_v1_single_response(self, sale_order, context=False): + APPROVAL_STEP = { + 'company': 1, + 'cust_manager': 2, + 'cust_director': 3, + } + data = { 'token': self.env['rest.api'].md5_salt(sale_order.id, 'sale.order'), 'id': sale_order.id, @@ -17,6 +23,7 @@ class SaleOrder(models.Model): 'purchase_order_file': True if sale_order.partner_purchase_order_file else False, 'invoice_count': sale_order.invoice_count, 'status': 'draft', + 'approval_step': APPROVAL_STEP[sale_order.web_approval] if sale_order.web_approval else 0, 'date_order': self.env['rest.api'].datetime_to_str(sale_order.date_order, '%d/%m/%Y %H:%M:%S'), 'pickings': [] } diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index a7302245..467d3be9 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -23,6 +23,12 @@ class ResPartner(models.Model): digital_invoice_tax = fields.Boolean(string="Digital Invoice & Faktur Pajak") is_potential = fields.Boolean(string='Potential') pakta_integritas = fields.Boolean(string='Pakta Integritas') + + use_so_approval = fields.Boolean(string='Use SO Approval') + web_role = fields.Selection([ + ('manager', 'Manager'), + ('director', 'Director') + ], string='Web Role') def get_child_ids(self): partner = self.env['res.partner'].search([('id', '=', self.id)], limit=1) @@ -31,6 +37,15 @@ class ResPartner(models.Model): partner_child_ids += [x['id'] for x in partner.parent_id.child_ids] partner_child_ids += [partner.parent_id.id] return partner_child_ids + + def get_approve_partner_ids(self, type=False): + parent = self.parent_id or self + partners = self.search([('parent_id', '=', parent.id), ('web_role', 'in', ['manager', 'director'])]) + + if type == 'email_comma_sep': + return ",".join([x.email for x in partners]) + + return partners @api.constrains('kota_id') def update_product_solr_flag(self): diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 759329b0..b7578da0 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -88,6 +88,11 @@ class SaleOrder(models.Model): picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') eta_date = fields.Datetime(string='ETA Date', copy=False, compute='_compute_eta_date') + web_approval = fields.Selection([ + ('company', 'Company'), + ('cust_manager', 'Customer Manager'), + ('cust_director', 'Customer Director') + ], string='Web Approval', copy=False) def _compute_eta_date(self): max_leadtime = 0 @@ -445,6 +450,13 @@ class SaleOrder(models.Model): order._validate_order() order.order_line.validate_line() + main_parent = order.partner_id.get_main_parent() + SYSTEM_UID = 25 + FROM_WEBSITE = order.create_uid.id == SYSTEM_UID + + if FROM_WEBSITE and main_parent.use_so_approval and order.web_approval != 'cust_director': + raise UserError("This order not yet approved by customer director") + if order.validate_partner_invoice_due(): return self._create_notification_action('Notification', 'Terdapat invoice yang telah melewati batas waktu, mohon perbarui pada dokumen Due Extension') @@ -472,6 +484,9 @@ class SaleOrder(models.Model): raise UserError("Invoice harus di Cancel dahulu") elif self.have_outstanding_picking: raise UserError("DO harus di Cancel dahulu") + + if not self.web_approval: + self.web_approval = 'company' # elif self.have_outstanding_po: # raise UserError("PO harus di Cancel dahulu") @@ -631,7 +646,23 @@ class SaleOrder(models.Model): line.discount = line.initial_discount line.initial_discount = False - + def action_web_approve(self): + if self.env.uid != self.partner_id.user_id.id: + raise UserError('You are not authorized to approve this order. Only %s can approve this order.' % self.partner_id.user_id.name) + + self.web_approval = 'company' + template = self.env.ref('indoteknik_custom.mail_template_sale_order_web_approve_notification') + template.send_mail(self.id, force_send=True) + + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': 'Notification', + 'message': 'Berhasil approve web order', + 'next': {'type': 'ir.actions.act_window_close'}, + } + } diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index da2dec99..370fcf7b 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -26,6 +26,10 @@ <group name="purchase" position="inside"> <field name="leadtime"/> </group> + <field name="vat" position="after"> + <field name="use_so_approval" attrs="{'invisible': [('parent_id', '!=', False), ('company_type', '!=', 'company')]}" /> + <field name="web_role" attrs="{'invisible': ['|', ('parent_id', '=', False), ('company_type', '=', 'company')]}" /> + </field> </field> </record> </data> diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 64e6ad7b..97726ed7 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -16,10 +16,16 @@ type="object" attrs="{'invisible': [('approval_status', 'in', ['pengajuan1', 'pengajuan2', 'approved'])]}" /> + <button name="action_web_approve" + string="Web Approve" + type="object" + attrs="{'invisible': ['|', '|', ('create_uid', '!=', 25), ('web_approval', '!=', False), ('state', '!=', 'draft')]}" + /> <button name="indoteknik_custom.action_view_uangmuka_penjualan" string="UangMuka" type="action" attrs="{'invisible': [('approval_status', '!=', 'approved')]}"/> </button> <field name="payment_term_id" position="after"> + <field name="create_uid" invisible="1"/> <field name="create_date" invisible="1"/> <field name="shipping_cost_covered" attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}"/> <field name="shipping_paid_by" attrs="{'required': ['|', ('create_date', '>', '2023-06-15'), ('create_date', '=', False)]}"/> @@ -142,6 +148,7 @@ <field name="partner_purchase_order_name" readonly="True"/> <field name="partner_purchase_order_description" readonly="True"/> <field name="partner_purchase_order_file" readonly="True"/> + <field name="web_approval" readonly="True"/> </group> <group> <button name="generate_payment_link_midtrans_sales_order" @@ -222,5 +229,43 @@ <field name="state">code</field> <field name="code">action = records.open_form_multi_update_status()</field> </record> + + <record id="mail_template_sale_order_web_approve_notification" model="mail.template"> + <field name="name">Sale Order: Web Approve Notification</field> + <field name="model_id" ref="indoteknik_custom.model_sale_order"/> + <field name="subject">Permintaan Persetujuan Pesanan ${object.name} di Indoteknik.com</field> + <field name="email_from">sales@indoteknik.com</field> + <field name="email_to">${object.partner_id.email | safe}</field> + <field name="email_cc">${object.partner_id.get_approve_partner_ids("email_comma_sep")}</field> + <field name="body_html" type="html"> + <table border="0" cellpadding="0" cellspacing="0" style="padding: 16px 0; background-color: #F1F1F1; font-family:Inter, Helvetica, Verdana, Arial,sans-serif; line-height: 24px; color: #454748; width: 100%; border-collapse:separate;"> + <tr><td align="center"> + <table border="0" cellpadding="0" cellspacing="0" width="590" style="font-size: 13px; padding: 16px; background-color: white; color: #454748; border-collapse:separate;"> + <tbody> + <tr> + <td align="center" style="min-width: 590px;"> + <table border="0" cellpadding="0" cellspacing="0" width="590" style="min-width: 590px; background-color: white; padding: 0px 8px 0px 8px; border-collapse:separate;"> + <tr><td style="padding-bottom: 24px;">Dear ${(object.partner_id.get_main_parent()).name},</td></tr> + + <tr><td style="padding-bottom: 16px;">Ini adalah konfirmasi pesanan dari ${object.partner_id.name | safe} untuk nomor pesanan ${object.name} yang memerlukan persetujuan agar dapat diproses.</td></tr> + <tr><td style="padding-bottom: 16px;"> + <a href="https://indoteknik.com/my/quotations/${object.id}" style="color: white; background-color: #C53030; border: none; border-radius: 6px; padding: 4px 8px; width: fit-content; display: block;"> + Lihat Pesanan + </a> + </td></tr> + <tr><td style="padding-bottom: 16px;">Mohon segera melakukan tinjauan terhadap pesanan ini dan memberikan persetujuan. Terima kasih atas perhatian dan kerjasama Anda. Kami berharap dapat segera melanjutkan proses pesanan ini setelah mendapatkan persetujuan Anda.</td></tr> + + <tr><td style="padding-bottom: 2px;">Hormat kami,</td></tr> + <tr><td style="padding-bottom: 2px;">PT. Indoteknik Dotcom Gemilang</td></tr> + <tr><td style="padding-bottom: 2px;">sales@indoteknik.com</td></tr> + </table> + </td> + </tr> + </tbody> + </table> + </td></tr> + </table> + </field> + </record> </data> </odoo>
\ No newline at end of file |
