From 01507fad2a05124137541dc187a06631732c606e Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 11 Dec 2024 15:14:31 +0700 Subject: master vendor sla --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/vendor_sla.py | 26 ++++++++++++++++ indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/vendor_sla.xml | 42 ++++++++++++++++++++++++++ indoteknik_custom/views/x_banner_category.xml | 2 +- 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 indoteknik_custom/models/vendor_sla.py create mode 100644 indoteknik_custom/views/vendor_sla.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index 89f62524..5f77d2de 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -155,6 +155,7 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'views/vendor_sla.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index ad6d75dd..70a84bd0 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -134,3 +134,4 @@ from . import find_page from . import approval_retur_picking from . import va_multi_approve from . import va_multi_reject +from . import vendor_sla diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py new file mode 100644 index 00000000..1e0a344f --- /dev/null +++ b/indoteknik_custom/models/vendor_sla.py @@ -0,0 +1,26 @@ +from odoo import models, fields, api + +class VendorSLA(models.Model): + _name = 'vendor.sla' + _description = 'Vendor SLA' + _rec_name = 'id_vendor' + + id_vendor = fields.Many2one('res.partner', string='Name') + duration = fields.Integer(string='Duration', description='Duration SLA') + unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="Unit" + ) + duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") + + + @api.depends('duration', 'unit') + def _compute_duration_unit(self): + for record in self: + if record.duration and record.unit: + record.duration_unit = f"{record.duration} {record.unit}" + else: + record.duration_unit = "" + + + \ 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 2375df9d..dcfee14b 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -148,3 +148,4 @@ access_sales_order_fulfillment_v2,access.sales.order.fulfillment.v2,model_sales_ access_v_move_outstanding,access.v.move.outstanding,model_v_move_outstanding,,1,1,1,1 access_va_multi_approve,access.va.multi.approve,model_va_multi_approve,,1,1,1,1 access_va_multi_reject,access.va.multi.reject,model_va_multi_reject,,1,1,1,1 +access_vendor_sla,access.vendor_sla,model_vendor_sla,,1,1,1,1 diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml new file mode 100644 index 00000000..d0e7f3e6 --- /dev/null +++ b/indoteknik_custom/views/vendor_sla.xml @@ -0,0 +1,42 @@ + + + + Vendor SLA + vendor.sla + tree,form + + + + Vendor SLA + vendor.sla + + + + + + + + + + Vendor SLA + vendor.sla + +
+ + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/indoteknik_custom/views/x_banner_category.xml b/indoteknik_custom/views/x_banner_category.xml index 11feb207..a83c4129 100755 --- a/indoteknik_custom/views/x_banner_category.xml +++ b/indoteknik_custom/views/x_banner_category.xml @@ -23,7 +23,7 @@ - + -- cgit v1.2.3 From 3ba967de0aa6c70b0d070a7ca723d49008aa0dd2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 13 Dec 2024 14:15:17 +0700 Subject: product sla --- indoteknik_custom/models/product_sla.py | 15 +++++++++++++++ indoteknik_custom/models/vendor_sla.py | 6 +++--- indoteknik_custom/views/product_sla.xml | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 2e663d30..05210cdc 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,6 +12,8 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') + vendor_id = fields.Many2one('res.partner',string='Vendor', readonly=True) + sla_vendor = fields.Char(string='SLA Vendor', readonly=True) avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) leadtime = fields.Char(string='Leadtime', readonly=True) sla = fields.Char(string='SLA', readonly=True) @@ -36,6 +38,7 @@ class ProductSla(models.Model): product.sla_version += 1 product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) + print(product_sla.id, product.id) if not product_sla: product_sla = self.env['product.sla'].create({ 'product_variant_id': product.id, @@ -48,6 +51,18 @@ class ProductSla(models.Model): self.sla = '-' product = self.product_variant_id + + q_vendor = [ + ('product_id', '=', product.id), + ('is_winner', '=', True) + ] + + vendor = self.env['purchase.pricelist'].search(q_vendor) + print(vendor.vendor_id.id) + if vendor: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + self.sla_vendor = "{} {}".format(vendor_sla.duration, vendor_sla.unit) + self.vendor_id = vendor.vendor_id.id qty_available = 0 qty_available = product.qty_onhand_bandengan diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 1e0a344f..9af86a14 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -5,11 +5,11 @@ class VendorSLA(models.Model): _description = 'Vendor SLA' _rec_name = 'id_vendor' - id_vendor = fields.Many2one('res.partner', string='Name') - duration = fields.Integer(string='Duration', description='Duration SLA') + id_vendor = fields.Many2one('res.partner', string='Name', domain="[('industry_id', '=', 46)]") + duration = fields.Integer(string='Duration', description='SLA Duration') unit = fields.Selection( [('jam', 'Jam'),('hari', 'Hari')], - string="Unit" + string="SLA Time" ) duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 8b0e874b..5276bb03 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,6 +6,8 @@ + + -- cgit v1.2.3 From 60f54931f8eac477ab737abab1710789e0a2aaf4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 30 Dec 2024 16:30:20 +0700 Subject: vendor sla --- indoteknik_custom/models/product_sla.py | 30 ++++++++++++++++++++++-------- indoteknik_custom/views/vendor_sla.xml | 2 +- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 05210cdc..f597ec44 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -25,6 +25,7 @@ class ProductSla(models.Model): def generate_product_variant_id_sla(self, limit=5000): # Filter produk non-Altama + products = self.env['product.product'].search([ ('x_manufacture', 'not in', [10, 122, 89]), ('location_id', '=', 57), @@ -58,10 +59,19 @@ class ProductSla(models.Model): ] vendor = self.env['purchase.pricelist'].search(q_vendor) + + vendor_duration = 0 print(vendor.vendor_id.id) if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) - self.sla_vendor = "{} {}".format(vendor_sla.duration, vendor_sla.unit) + sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 + if sla_vendor > 0: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration + + self.sla_vendor = vendor_sla.duration_unit self.vendor_id = vendor.vendor_id.id qty_available = 0 @@ -90,10 +100,14 @@ class ProductSla(models.Model): if len(leadtimes) > 0: avg_leadtime = sum(leadtimes) / len(leadtimes) rounded_leadtime = math.ceil(avg_leadtime) - self.avg_leadtime = rounded_leadtime - if rounded_leadtime >= 1 and rounded_leadtime <= 5: - self.sla = '3-7 Hari' - elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - self.sla = '4-12 Hari' - elif rounded_leadtime >= 11: - self.sla = 'Indent' \ No newline at end of file + estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = estimation_sla_days + self.avg_leadtime = int(rounded_leadtime) + # self.sla = (sla_vendor + self.avg_leadtime) / 2 + # if rounded_leadtime >= 1 and rounded_leadtime <= 5: + # self.sla = '3-7 Hari' + # elif rounded_leadtime >= 6 and rounded_leadtime <= 10: + # self.sla = '4-12 Hari' + # elif rounded_leadtime >= 11: + # self.sla = 'Indent' \ No newline at end of file diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml index d0e7f3e6..cf4425eb 100644 --- a/indoteknik_custom/views/vendor_sla.xml +++ b/indoteknik_custom/views/vendor_sla.xml @@ -26,7 +26,7 @@ - + -- cgit v1.2.3 From 7ac434ec0fcf75cb6eefe1892118b7c18b3db53a Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 7 Jan 2025 17:02:06 +0700 Subject: sla --- indoteknik_api/controllers/api_v1/product.py | 23 ++++++++++----- indoteknik_custom/models/product_sla.py | 44 ++++++++++++---------------- indoteknik_custom/views/product_sla.xml | 4 +-- 3 files changed, 36 insertions(+), 35 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 32362582..2573d7a8 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -35,7 +35,7 @@ class Product(controller.Controller): return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) - @controller.Controller.must_authorized() + @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): id = int(kw.get('id')) date_7_days_ago = datetime.now() - timedelta(days=7) @@ -47,12 +47,19 @@ class Product(controller.Controller): ('product_variant_id', '=', id), ('write_date', '>=', date_7_days_ago.strftime("%Y-%m-%d %H:%M:%S")) ], limit=1) + + include_instant = False qty_available = product.qty_free_bandengan - - if qty_available < 0: - qty_available = 0 - + + + if qty_available > 0 : + include_instant = True + else : + qty_available = 0 + if product_sla.sla_vendor_id.unit == 'jam': + include_instant = True + qty = 0 sla_date = '-' @@ -74,9 +81,10 @@ class Product(controller.Controller): if qty_available > 0: qty = qty_available + total_adem + total_excell + sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = '2-4 Hari' + sla_date = product.sla else: sla_date = '3-7 Hari' except: @@ -84,7 +92,7 @@ class Product(controller.Controller): else: if qty_available > 0: qty = qty_available - sla_date = product_sla.sla or '-' + sla_date = product_sla.sla or 'Indent' elif qty_vendor > 0: qty = total_excell sla_date = '2-4 Hari' @@ -92,6 +100,7 @@ class Product(controller.Controller): data = { 'qty': qty, 'sla_date': sla_date, + 'can_instant': include_instant } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index f597ec44..dfdf7662 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,8 +12,8 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') - vendor_id = fields.Many2one('res.partner',string='Vendor', readonly=True) - sla_vendor = fields.Char(string='SLA Vendor', readonly=True) + sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) + sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) leadtime = fields.Char(string='Leadtime', readonly=True) sla = fields.Char(string='SLA', readonly=True) @@ -52,7 +52,14 @@ class ProductSla(models.Model): self.sla = '-' product = self.product_variant_id + + # qty_available = 0 + # qty_available = product.qty_onhand_bandengan + + # if qty_available > 0: + # self.sla = 1 + q_vendor = [ ('product_id', '=', product.id), ('is_winner', '=', True) @@ -61,7 +68,6 @@ class ProductSla(models.Model): vendor = self.env['purchase.pricelist'].search(q_vendor) vendor_duration = 0 - print(vendor.vendor_id.id) if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 @@ -69,17 +75,9 @@ class ProductSla(models.Model): if vendor_sla.unit == 'hari': vendor_duration = vendor_sla.duration * 24 * 60 else : - vendor_duration = vendor_sla.duration - - self.sla_vendor = vendor_sla.duration_unit - self.vendor_id = vendor.vendor_id.id - - qty_available = 0 - qty_available = product.qty_onhand_bandengan - - - if qty_available > 0: - self.sla = '1 Hari' + vendor_duration = vendor_sla.duration * 60 + + self.sla_vendor_id = vendor_sla.id if vendor_sla else False query = [ ('product_id', '=', product.id), @@ -88,12 +86,13 @@ class ProductSla(models.Model): ('picking_id.state', 'not in', ['cancel']) ] picking = self.env['stock.move.line'].search(query) + leadtimes=[] for stock in picking: date_delivered = stock.picking_id.driver_departure_date - date_so_confirmed = stock.picking_id.sale_id.date_order - if date_delivered and date_so_confirmed: - leadtime = date_delivered - date_so_confirmed + date_do_ready = stock.picking_id.date_reserved + if date_delivered and date_do_ready: + leadtime = date_delivered - date_do_ready leadtime_in_days = leadtime.days leadtimes.append(leadtime_in_days) @@ -102,12 +101,5 @@ class ProductSla(models.Model): rounded_leadtime = math.ceil(avg_leadtime) estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration estimation_sla_days = estimation_sla / (24 * 60) - self.sla = estimation_sla_days - self.avg_leadtime = int(rounded_leadtime) - # self.sla = (sla_vendor + self.avg_leadtime) / 2 - # if rounded_leadtime >= 1 and rounded_leadtime <= 5: - # self.sla = '3-7 Hari' - # elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - # self.sla = '4-12 Hari' - # elif rounded_leadtime >= 11: - # self.sla = 'Indent' \ No newline at end of file + self.sla = math.ceil(estimation_sla_days) + self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 5276bb03..3722ef3d 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,8 +6,8 @@ - - + + -- cgit v1.2.3 From 63878bd84a6eb9094e702963d7c78fcd8dfa1808 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 10 Jan 2025 10:44:59 +0700 Subject: api sla --- indoteknik_api/controllers/api_v1/product.py | 59 ++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 2573d7a8..7570015f 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -1,6 +1,6 @@ from .. import controller from odoo import http -from odoo.http import request +from odoo.http import request, Response from datetime import datetime, timedelta import ast import logging @@ -33,7 +33,58 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - + + @http.route(prefix + 'product/variants/sla', auth='none', type='json', csrf=False, cors='*', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() + def get_product_template_sla_by_id(self, **kw): + json_raw = json.loads(request.httprequest.data) + + ids = json_raw.get('ids') + ids = list(map(int, ids)) + + if not ids or not isinstance(ids, list): + return ({'status' : 'Failed','message': 'Parameter "ids" harus berupa list dan tidak boleh kosong.'}) + + sla_days = 0 + products = request.env['product.product'].search([('id', 'in', ids)]) + if len(products) < 1: + return ({ + 'status' : 'Failed', + 'message' : 'Produk Tidak Di Temukan.' + }) + + product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) + if len(product_slas) < 1: + return ({ + 'status' : 'Failed', + 'message' : 'Produk Tidak Di Temukan.' + }) + + # Mapping SLA untuk mempermudah lookup + sla_map = {sla.product_variant_id.id: sla for sla in product_slas} + + for product in products: + product_sla = sla_map.get(product.id) + if product_sla: + sla_days = max(sla_days, product_sla.sla_vendor_id.duration) + if product.qty_free_bandengan < 1 : + if product_sla.sla_vendor_id.unit != 'jam': + return ({ + 'status' : 'Success', + 'data' : [{ + 'include_instant': False, + 'sla_days': sla_days + }], + }) + # Jika semua loop selesai tanpa include_instant menjadi False + return ({ + 'status' : 'Success', + 'data' : [{ + 'include_instant': True, + 'sla_days': sla_days + }], + }) + @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): @@ -84,9 +135,9 @@ class Product(controller.Controller): sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = product.sla + sla_date = product_sla.sla else: - sla_date = '3-7 Hari' + sla_date = product_sla.sla except: print('error') else: -- cgit v1.2.3 From e3e7f29ad939a774878316e46e10a5c1370fda77 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 16 Jan 2025 15:53:36 +0700 Subject: sla --- indoteknik_custom/models/product_sla.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index dfdf7662..988aa78f 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -99,7 +99,7 @@ class ProductSla(models.Model): if len(leadtimes) > 0: avg_leadtime = sum(leadtimes) / len(leadtimes) rounded_leadtime = math.ceil(avg_leadtime) - estimation_sla = (rounded_leadtime * 24 * 60) + vendor_duration + estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 estimation_sla_days = estimation_sla / (24 * 60) self.sla = math.ceil(estimation_sla_days) self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file -- cgit v1.2.3 From 2644e259339cd921babf49218aadbdcedb0c8937 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 28 Jan 2025 09:46:48 +0700 Subject: change product sla --- indoteknik_api/controllers/api_v1/product.py | 72 ++++++++--------- indoteknik_custom/models/product_sla.py | 114 +++++++++++++++------------ indoteknik_custom/views/product_sla.xml | 5 +- 3 files changed, 100 insertions(+), 91 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 7570015f..5d564ff9 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -34,31 +34,34 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - @http.route(prefix + 'product/variants/sla', auth='none', type='json', csrf=False, cors='*', methods=['GET', 'OPTIONS']) + @http.route(prefix + 'product/variants/sla', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() - def get_product_template_sla_by_id(self, **kw): - json_raw = json.loads(request.httprequest.data) + def get_product_template_sla_by_id(self, **kwargs): + body_params = kwargs.get('ids') - ids = json_raw.get('ids') - ids = list(map(int, ids)) + if not body_params: + return self.response('Failed', code=400, description='id is required') - if not ids or not isinstance(ids, list): - return ({'status' : 'Failed','message': 'Parameter "ids" harus berupa list dan tidak boleh kosong.'}) + ids = [int(id.strip()) for id in body_params.split(',') if id.strip().isdigit()] - sla_days = 0 + sla_duration = 0 + sla_unit = 'Hari' + include_instant = True products = request.env['product.product'].search([('id', 'in', ids)]) if len(products) < 1: - return ({ - 'status' : 'Failed', - 'message' : 'Produk Tidak Di Temukan.' - }) + return self.response( + 'Failed', + code=400, + description='Produk Tidak Di Temukan.' + ) product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) if len(product_slas) < 1: - return ({ - 'status' : 'Failed', - 'message' : 'Produk Tidak Di Temukan.' - }) + return self.response( + 'Failed', + code=400, + description='SLA Tidak Di Temukan.' + ) # Mapping SLA untuk mempermudah lookup sla_map = {sla.product_variant_id.id: sla for sla in product_slas} @@ -66,24 +69,20 @@ class Product(controller.Controller): for product in products: product_sla = sla_map.get(product.id) if product_sla: - sla_days = max(sla_days, product_sla.sla_vendor_id.duration) + sla_duration = max(sla_duration, int(product_sla.sla)) + sla_unit = product_sla.sla_vendor_id.unit if product.qty_free_bandengan < 1 : if product_sla.sla_vendor_id.unit != 'jam': - return ({ - 'status' : 'Success', - 'data' : [{ - 'include_instant': False, - 'sla_days': sla_days - }], - }) + include_instant = False + break + # Jika semua loop selesai tanpa include_instant menjadi False - return ({ - 'status' : 'Success', - 'data' : [{ - 'include_instant': True, - 'sla_days': sla_days - }], - }) + return self.response({ + 'include_instant': include_instant, + 'sla_duration': sla_duration, + 'sla_unit': sla_unit + } + ) @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() @@ -98,18 +97,12 @@ class Product(controller.Controller): ('product_variant_id', '=', id), ('write_date', '>=', date_7_days_ago.strftime("%Y-%m-%d %H:%M:%S")) ], limit=1) - - include_instant = False qty_available = product.qty_free_bandengan - if qty_available > 0 : - include_instant = True - else : + if qty_available < 1 : qty_available = 0 - if product_sla.sla_vendor_id.unit == 'jam': - include_instant = True qty = 0 sla_date = '-' @@ -150,8 +143,7 @@ class Product(controller.Controller): data = { 'qty': qty, - 'sla_date': sla_date, - 'can_instant': include_instant + 'sla_date': sla_date } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 988aa78f..04ad2ffd 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -14,51 +14,53 @@ class ProductSla(models.Model): product_variant_id = fields.Many2one('product.product',string='Product') sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') - avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) - leadtime = fields.Char(string='Leadtime', readonly=True) + sla_logistic = fields.Char(string='SLA Logistic', readonly=True) + sla_logistic_unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Logistic Time" + ) + sla_logistic_duration_unit = fields.Char(string="SLA Logistic Duration (Unit)") sla = fields.Char(string='SLA', readonly=True) version = fields.Integer(string="Version", compute="_compute_version") def _compute_version(self): for sla in self: sla.version = sla.product_variant_id.sla_version + + - def generate_product_variant_id_sla(self, limit=5000): - # Filter produk non-Altama - + def generate_product_variant_id_sla(self, limit=500): + offset = 0 + # while True: products = self.env['product.product'].search([ - ('x_manufacture', 'not in', [10, 122, 89]), - ('location_id', '=', 57), - ('stock_move_ids', '!=', False), - ], order='sla_version asc', limit=limit) + ('active', '=', True), + ('sale_ok', '=', True), + ], order='sla_version asc', limit=limit, offset=offset) + + # if not products: + # break - i = 1 for product in products: - _logger.info(f'Product SLA: {i}/{len(products)}') - i += 1 - product.sla_version += 1 + _logger.info(f'Memproses SLA untuk produk ID {product.id}, versi {product.sla_version}') product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) - print(product_sla.id, product.id) if not product_sla: - product_sla = self.env['product.sla'].create({ - 'product_variant_id': product.id, - }) - + product_sla = self.create({'product_variant_id': product.id}) + product_sla.generate_product_sla() + # Tandai produk sebagai sudah diproses + product.sla_version += 1 + + offset += limit + + def generate_product_sla(self): - self.avg_leadtime = '-' - self.sla = '-' + # self.sla_logistic = '-' + # self.sla_logistic_duration_unit = '-' + # self.sla = '-' product = self.product_variant_id - - # qty_available = 0 - # qty_available = product.qty_onhand_bandengan - - - # if qty_available > 0: - # self.sla = 1 q_vendor = [ ('product_id', '=', product.id), @@ -68,6 +70,8 @@ class ProductSla(models.Model): vendor = self.env['purchase.pricelist'].search(q_vendor) vendor_duration = 0 + + #SLA Vendor if vendor: vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 @@ -78,28 +82,40 @@ class ProductSla(models.Model): vendor_duration = vendor_sla.duration * 60 self.sla_vendor_id = vendor_sla.id if vendor_sla else False + #SLA Logistik selalu 1 hari + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = math.ceil(estimation_sla_days) + self.sla_logistic = int(1) + self.sla_logistic_unit = 'hari' + self.sla_logistic_duration_unit = '1 hari' + else: + self.unlink() + else: + self.unlink() + - query = [ - ('product_id', '=', product.id), - ('picking_id', '!=', False), - ('picking_id.location_id', '=', 57), - ('picking_id.state', 'not in', ['cancel']) - ] - picking = self.env['stock.move.line'].search(query) + # query = [ + # ('product_id', '=', product.id), + # ('picking_id', '!=', False), + # ('picking_id.location_id', '=', 57), + # ('picking_id.state', 'not in', ['cancel']) + # ] + # picking = self.env['stock.move.line'].search(query) - leadtimes=[] - for stock in picking: - date_delivered = stock.picking_id.driver_departure_date - date_do_ready = stock.picking_id.date_reserved - if date_delivered and date_do_ready: - leadtime = date_delivered - date_do_ready - leadtime_in_days = leadtime.days - leadtimes.append(leadtime_in_days) + # leadtimes=[] + # for stock in picking: + # date_delivered = stock.picking_id.driver_departure_date + # date_do_ready = stock.picking_id.date_reserved + # if date_delivered and date_do_ready: + # leadtime = date_delivered - date_do_ready + # leadtime_in_days = leadtime.days + # leadtimes.append(leadtime_in_days) - if len(leadtimes) > 0: - avg_leadtime = sum(leadtimes) / len(leadtimes) - rounded_leadtime = math.ceil(avg_leadtime) - estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 - estimation_sla_days = estimation_sla / (24 * 60) - self.sla = math.ceil(estimation_sla_days) - self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file + # if len(leadtimes) > 0: + # avg_leadtime = sum(leadtimes) / len(leadtimes) + # rounded_leadtime = math.ceil(avg_leadtime) + # estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 + # estimation_sla_days = estimation_sla / (24 * 60) + # self.sla = math.ceil(estimation_sla_days) + # self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 3722ef3d..9179730f 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -8,7 +8,7 @@ - + @@ -23,7 +23,8 @@ - + + -- cgit v1.2.3 From 346b6dc89cbde3640413714175cdc438544a664c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 28 Jan 2025 14:58:05 +0700 Subject: save response --- indoteknik_custom/models/stock_picking.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index cd330aeb..6967e1a3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -165,6 +165,11 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + # Biteship Section + biteship_id = fields.Char(string="Biteship Respon ID") + biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") + biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + def _compute_lalamove_image_html(self): for record in self: if record.lalamove_image_url: @@ -387,7 +392,7 @@ class StockPicking(models.Model): } # Cek jika pengiriman instant atau same_day - if "instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type: + if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ "origin_note": "BELAKANG INDOMARET", "courier_company": self.carrier_id.name.lower(), @@ -404,10 +409,19 @@ class StockPicking(models.Model): # Kirim request ke Biteship response = requests.post(url, headers=headers, json=payload) - if response.status_code == 201: - return response.json() + if response.status_code == 200: + data = response.json() + + self.biteship_id = data.get("id", "") + self.biteship_tracking_id = data.get("tracking_id", "") + self.biteship_waybill_id = data.get("waybill_id", "") + + return data else: - raise UserError(f"Error saat mengirim ke Biteship: {response.content}") + error_data = response.json() + error_message = error_data.get("error", "Unknown error") + error_code = error_data.get("code", "No code provided") + raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): -- cgit v1.2.3 From 74796f3390ad90e2ae981351cd0491aca6dccc9c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 31 Jan 2025 16:45:07 +0700 Subject: add ready to ship date --- indoteknik_custom/models/product_template.py | 13 +++++++ indoteknik_custom/models/sale_order.py | 57 +++++++++++++++++++++++++++- indoteknik_custom/models/stock_picking.py | 29 +++++++++++++- indoteknik_custom/views/product_product.xml | 8 ++++ indoteknik_custom/views/sale_order.xml | 1 + indoteknik_custom/views/stock_picking.xml | 5 ++- 6 files changed, 109 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 5bedae13..8c57774e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -403,6 +403,19 @@ class ProductProduct(models.Model): merchandise_ok = fields.Boolean(string='Product Promotion') qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + def generate_product_sla(self): + product_variant_ids = self.env.context.get('active_ids', []) + product_variant = self.search([('id', 'in', product_variant_ids)]) + sla_record = self.env['product.sla'].search([('product_variant_id', '=', product_variant.id)], limit=1) + + if sla_record: + sla_record.generate_product_sla() + else: + new_sla_record = self.env['product.sla'].create({ + 'product_variant_id': product_variant.id, + }) + new_sla_record.generate_product_sla() + def _compute_qr_code_variant(self): for rec in self: # Skip inactive variants diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 48195b77..32e6f11f 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -144,6 +144,16 @@ class SaleOrder(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) + estimated_ready_ship_date = fields.Datetime( + string='ET Ready to Ship', + copy=False, + store=True + ) + expected_ready_ship_date = fields.Datetime( + string='ET Ready to Ship FIX', + copy=False, + store=True + ) @api.onchange('payment_status') def _is_continue_transaction(self): @@ -377,6 +387,45 @@ class SaleOrder(models.Model): else: rec.eta_date = False + def _compute_etrts_date(self): + max_slatime = 100 + + # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke + max_leadtime = 0 + + for line in self.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)]) + slatime = int(product_sla.sla) or 1 + max_slatime = max(max_slatime, slatime) + + for rec in self: + if rec.date_order: + eta_date = datetime.now() + timedelta(days=max_slatime) + rec.estimated_ready_ship_date = eta_date + if not rec.expected_ready_ship_date: + rec.expected_ready_ship_date = eta_date + # else: + # rec.estimated_ready_ship_date = False + + def _set_etrts_date(self): + for order in self: + if order.state in ('done', 'cancel', 'sale'): + raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) + # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) + + @api.onchange('estimated_ready_ship_date') + def _onchange_estimated_ready_ship_date(self): + for record in self: + if (record.estimated_ready_ship_date and record.expected_ready_ship_date): + if(record.estimated_ready_ship_date < record.expected_ready_ship_date): + return { + 'warning': { + 'title': _('Requested date is too soon.'), + 'message': _("The delivery date is sooner than the expected date." + "You may be unable to honor the delivery date.") + } + } + def _prepare_invoice(self): """ Prepare the dict of values to create the new invoice for a sales order. This method may be @@ -573,7 +622,7 @@ class SaleOrder(models.Model): def write(self, vals): res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': @@ -1009,6 +1058,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() + order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() @@ -1383,13 +1433,14 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) + order._compute_etrts_date() # order._update_partner_details() return order def write(self, vals): # Call the super method to handle the write operation res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() # Check if the update is coming from a save operation # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): # self._update_partner_details() @@ -1424,4 +1475,6 @@ class SaleOrder(models.Model): raise UserError( "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") res = super(SaleOrder, self).write(vals) + if 'order_line' in vals: + self._compute_etrts_date() return res \ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6967e1a3..f766dc3f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero -from datetime import timedelta, datetime +from datetime import timedelta, datetime as waktu from itertools import groupby import pytz, requests, json, requests from dateutil import parser @@ -169,6 +169,33 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', store=True, default=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') + + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_hours(self): + for record in self: + if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # Gunakan nilai yang sangat besar sebagai placeholder + record.countdown_hours = 999999 + else: + delta = record.estimated_ready_ship_date - waktu.now() + record.countdown_hours = delta.total_seconds() / 3600 + + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_ready_to_ship(self): + for record in self: + if record.state in ('cancel', 'done'): + record.countdown_ready_to_ship = False + else: + if record.estimated_ready_ship_date: + delta = record.estimated_ready_ship_date - waktu.now() + days = delta.days + hours, remainder = divmod(delta.seconds, 3600) + record.countdown_ready_to_ship = f"{days} days, {hours} hours" + else: + record.countdown_ready_to_ship = False def _compute_lalamove_image_html(self): for record in self: diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml index 71748e44..b214dc87 100644 --- a/indoteknik_custom/views/product_product.xml +++ b/indoteknik_custom/views/product_product.xml @@ -31,6 +31,14 @@ model.action_sync_to_solr() + + Generate Product SLA + + + code + model.generate_product_sla() + + Sync Variant To Solr: Solr Flag 2 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 008a04ed..09a71912 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -69,6 +69,7 @@ + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 8acba608..ae6ae940 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,7 +7,8 @@ - create_date desc + countdown_hours asc + @@ -18,6 +19,8 @@ + + -- cgit v1.2.3 From d0647f4b4a0df94c7b51852823df37eeb5b89e3e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 3 Feb 2025 14:45:18 +0700 Subject: add generate vendor sla --- indoteknik_custom/models/vendor_sla.py | 75 ++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 9af86a14..67b6ffc3 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -1,4 +1,7 @@ from odoo import models, fields, api +import logging +import math +_logger = logging.getLogger(__name__) class VendorSLA(models.Model): _name = 'vendor.sla' @@ -12,15 +15,81 @@ class VendorSLA(models.Model): string="SLA Time" ) duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") - - + + # pertama, lakukan group by vendor pada modul purchase.order + # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order + # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration + def generate_vendor_id_sla(self): + # Step 1: Group purchase orders by vendor (partner_id) + po_env = self.env['purchase.order'] + pos = po_env.read_group( + domain=[('state', '=', 'done')], + fields=['partner_id', 'date_planned', 'date_approve'], + groupby=['partner_id'], + lazy=False + ) + + for group in pos: + partner_id = group['partner_id'][0] + total_duration = 0 + count = 0 + + # Step 2: Calculate the average duration for each vendor + pos_for_vendor = po_env.search([ + ('partner_id', '=', partner_id), + ('state', '=', 'done'), + ('date_planned', '>=', '2023-01-01') + ]) + + for po in pos_for_vendor: + if po.date_planned and po.date_approve: + date_planned = fields.Datetime.to_datetime(po.date_planned) + date_approve = fields.Datetime.to_datetime(po.date_approve) + if date_planned < date_approve: continue + duration = (date_planned - date_approve).total_seconds() / 3600 # Convert to hours + total_duration += duration + count += 1 + + if count > 0: + average_duration = total_duration / count + + # Step 3: Update the duration field in the corresponding res.partner record + vendor_sla = self.search([('id_vendor', '=', partner_id)], limit=1) + + # Konversi jam ke hari jika diperlukan + if average_duration >= 24: + days = average_duration / 24 + if days - int(days) > 0.5: # Jika sisa lebih dari 0,5, bulatkan ke atas + days = int(days) + 1 + else: # Jika sisa 0,5 atau kurang, bulatkan ke bawah + days = int(days) + duration_to_save = days + unit_to_save = 'hari' + else: + duration_to_save = round(average_duration) + unit_to_save = 'jam' + + # Update atau create vendor SLA record + if vendor_sla: + vendor_sla.write({ + 'duration': duration_to_save, + 'unit': unit_to_save + }) + else: + self.create({ + 'id_vendor': partner_id, + 'duration': duration_to_save, + 'unit': unit_to_save + }) + _logger.info(f'Proses SLA untuk Vendor selesai dilakukan') + @api.depends('duration', 'unit') def _compute_duration_unit(self): for record in self: if record.duration and record.unit: record.duration_unit = f"{record.duration} {record.unit}" else: - record.duration_unit = "" + record.duration_unit = "-" \ No newline at end of file -- cgit v1.2.3 From 88834d1fcda0914867fc6420ba8cb9b4046b3a11 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Feb 2025 16:38:59 +0700 Subject: add public holiday --- indoteknik_custom/__manifest__.py | 1 + indoteknik_custom/models/__init__.py | 1 + indoteknik_custom/models/public_holiday.py | 11 +++++ indoteknik_custom/models/sale_order.py | 3 +- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/public_holiday.xml | 57 ++++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 indoteknik_custom/models/public_holiday.py create mode 100644 indoteknik_custom/views/public_holiday.xml diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index c1593c9e..580f43de 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -159,6 +159,7 @@ 'report/report_sale_order.xml', 'views/vendor_sla.xml', 'views/coretax_faktur.xml', + 'views/public_holiday.xml', ], 'demo': [], 'css': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 43fbf146..4c1ef68c 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -140,3 +140,4 @@ from . import va_multi_reject from . import vendor_sla from . import stock_immediate_transfer from . import coretax_fatur +from . import public_holiday diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py new file mode 100644 index 00000000..70b7c53a --- /dev/null +++ b/indoteknik_custom/models/public_holiday.py @@ -0,0 +1,11 @@ +from odoo import api, fields, models +from datetime import timedelta, datetime + +class PublicHoliday(models.Model): + _name = 'hr.public.holiday' + _description = 'Public Holidays' + + name = fields.Char(string='Holiday Name', required=True) + start_date = fields.Date('Start Holiday Date', required=True) + end_date = fields.Date('End Holiday Date', required=True) + # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 32e6f11f..7eb4151a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -387,8 +387,7 @@ class SaleOrder(models.Model): else: rec.eta_date = False - def _compute_etrts_date(self): - max_slatime = 100 + max_slatime = 0 # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke max_leadtime = 0 diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index b6212f1b..1f3835fa 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -157,3 +157,4 @@ access_User_pengajuan_tempo_line,access.user.pengajuan.tempo.line,model_user_pen access_user_pengajuan_tempo,access.user.pengajuan.tempo,model_user_pengajuan_tempo,,1,1,1,1 access_reject_reason_wizard,reject.reason.wizard,model_reject_reason_wizard,,1,1,1,0 access_confirm_approval_wizard,confirm.approval.wizard,model_confirm_approval_wizard,,1,1,1,0 +access_hr_public_holiday,confirm.hr.public.holiday,model_hr_public_holiday,,1,1,1,0 diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml new file mode 100644 index 00000000..d0b9c5d5 --- /dev/null +++ b/indoteknik_custom/views/public_holiday.xml @@ -0,0 +1,57 @@ + + + + + + hr.public.holiday access + + + + + + + + + + + hr.public.holiday.form + hr.public.holiday + +
+ + + + + + + +
+
+
+ + + hr.public.holiday.tree + hr.public.holiday + + + + + + + + + + Public Holidays + hr.public.holiday + tree,form + + + +
+
-- cgit v1.2.3 From 0dbcd5eb060924a0860b2586776f65d5ce19b9ef Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 7 Feb 2025 15:07:01 +0700 Subject: estimation delerivery date --- indoteknik_api/controllers/api_v1/product.py | 18 ++++++- indoteknik_custom/models/sale_order.py | 77 +++++++++++++--------------- indoteknik_custom/views/sale_order.xml | 2 +- 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 5d564ff9..93ef305c 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -9,6 +9,17 @@ import json _logger = logging.getLogger(__name__) +def get_days_until_next_business_day(start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + + while today.weekday() >= 5 : + today += timedelta(days=1) + offset += 1 + + return offset + + class Product(controller.Controller): prefix = '/api/v1/' @@ -75,12 +86,17 @@ class Product(controller.Controller): if product_sla.sla_vendor_id.unit != 'jam': include_instant = False break + + start_date = datetime.today().date() + additional_days = get_days_until_next_business_day(start_date) # Jika semua loop selesai tanpa include_instant menjadi False return self.response({ 'include_instant': include_instant, 'sla_duration': sla_duration, - 'sla_unit': sla_unit + 'sla_additional_days': additional_days, + 'sla_total' : int(sla_duration) + int(additional_days), + 'sla_unit': 'Hari' if additional_days > 0 else sla_unit } ) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 32e6f11f..cae99447 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -146,10 +146,11 @@ class SaleOrder(models.Model): ]) estimated_ready_ship_date = fields.Datetime( string='ET Ready to Ship', - copy=False, + compute='_compute_etrts_date', store=True + ) - expected_ready_ship_date = fields.Datetime( + expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship FIX', copy=False, store=True @@ -373,39 +374,29 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True + @api.depends('date_order', 'estimated_arrival_days', 'state') def _compute_eta_date(self): - max_leadtime = 0 - - for line in self.order_line: - leadtime = line.vendor_id.leadtime - max_leadtime = max(max_leadtime, leadtime) - - for rec in self: - if rec.date_order and rec.state not in ['cancel', 'draft']: - eta_date = datetime.now() + timedelta(days=max_leadtime) - rec.eta_date = eta_date + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) else: rec.eta_date = False - def _compute_etrts_date(self): - max_slatime = 100 - - # untuk setiap produk dalam so di ambil sla nya sla paling kecil itulah yang dipakai untuk tambah ke - max_leadtime = 0 - - for line in self.order_line: - product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)]) - slatime = int(product_sla.sla) or 1 - max_slatime = max(max_slatime, slatime) - + @api.depends("order_line.product_id") + def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: + max_slatime = 1 # Default SLA jika tidak ada + for line in rec.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + max_slatime = max(max_slatime, slatime) + if rec.date_order: eta_date = datetime.now() + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date - if not rec.expected_ready_ship_date: - rec.expected_ready_ship_date = eta_date - # else: - # rec.estimated_ready_ship_date = False + # Jika expected_ready_to_ship kosong, set nilai default + if not rec.expected_ready_to_ship: + rec.expected_ready_to_ship = eta_date def _set_etrts_date(self): for order in self: @@ -413,19 +404,6 @@ class SaleOrder(models.Model): raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) - @api.onchange('estimated_ready_ship_date') - def _onchange_estimated_ready_ship_date(self): - for record in self: - if (record.estimated_ready_ship_date and record.expected_ready_ship_date): - if(record.estimated_ready_ship_date < record.expected_ready_ship_date): - return { - 'warning': { - 'title': _('Requested date is too soon.'), - 'message': _("The delivery date is sooner than the expected date." - "You may be unable to honor the delivery date.") - } - } - def _prepare_invoice(self): """ Prepare the dict of values to create the new invoice for a sales order. This method may be @@ -619,6 +597,21 @@ class SaleOrder(models.Model): if line.product_id.type == 'product': line_no += 1 line.line_no = line_no + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) def write(self, vals): res = super(SaleOrder, self).write(vals) @@ -627,7 +620,7 @@ class SaleOrder(models.Model): for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id - + return res def calculate_so_status(self): @@ -1433,7 +1426,7 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) - order._compute_etrts_date() + # order._compute_etrts_date() # order._update_partner_details() return order diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 09a71912..74fc6e54 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -69,7 +69,7 @@
- + -- cgit v1.2.3 From 6a1b03cbd12931784aee8226ed5f163dcae42081 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 13 Feb 2025 11:20:11 +0700 Subject: biteship --- indoteknik_custom/models/sale_order.py | 35 ++++++++++++----------- indoteknik_custom/models/stock_picking.py | 46 +++++++++++++++++-------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index cae99447..c6f8adc4 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -145,13 +145,13 @@ class SaleOrder(models.Model): ('NP', 'Non Pareto') ]) estimated_ready_ship_date = fields.Datetime( - string='ET Ready to Ship', + string='ET Ready to Ship compute', compute='_compute_etrts_date', store=True ) expected_ready_to_ship = fields.Datetime( - string='ET Ready to Ship FIX', + string='ET Ready to Ship', copy=False, store=True ) @@ -394,9 +394,26 @@ class SaleOrder(models.Model): if rec.date_order: eta_date = datetime.now() + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date + rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default if not rec.expected_ready_to_ship: rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + rec.commitment_date = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) def _set_etrts_date(self): for order in self: @@ -598,20 +615,6 @@ class SaleOrder(models.Model): line_no += 1 line.line_no = line_no - @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship - def _onchange_expected_ready_ship_date(self): - for rec in self: - if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: - # Hanya membandingkan tanggal saja, tanpa jam - expected_date = rec.expected_ready_to_ship.date() - estimated_date = rec.estimated_ready_ship_date.date() - - if expected_date < estimated_date: - rec.expected_ready_to_ship = rec.estimated_ready_ship_date - raise ValidationError( - "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." - .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) - ) def write(self, vals): res = super(SaleOrder, self).write(vals) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index f766dc3f..e7d9dbd5 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -170,18 +170,18 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', store=True, default=False) + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_ready_to_ship', store=True, default=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_hours(self): - for record in self: - if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # Gunakan nilai yang sangat besar sebagai placeholder - record.countdown_hours = 999999 - else: - delta = record.estimated_ready_ship_date - waktu.now() - record.countdown_hours = delta.total_seconds() / 3600 + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 @api.depends('estimated_ready_ship_date', 'state') def _compute_countdown_ready_to_ship(self): @@ -194,6 +194,7 @@ class StockPicking(models.Model): days = delta.days hours, remainder = divmod(delta.seconds, 3600) record.countdown_ready_to_ship = f"{days} days, {hours} hours" + record.countdown_hours = delta.total_seconds() / 3600 else: record.countdown_ready_to_ship = False @@ -351,7 +352,7 @@ class StockPicking(models.Model): picking.tracking_by = self.env.user.id ata_at_str = data.get("ata_at") envio_ata = self._convert_to_datetime(data.get("ata_at")) - + picking.driver_arrival_date = envio_ata if data.get("status") != 'delivered': picking.driver_arrival_date = False @@ -367,7 +368,7 @@ class StockPicking(models.Model): # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - + # Fungsi untuk membangun items_data dari order lines def build_items_data(lines): return [{ @@ -411,7 +412,8 @@ class StockPicking(models.Model): "destination_contact_phone": self.real_shipping_id.phone or self.real_shipping_id.mobile, "destination_address": self.real_shipping_id.street, "destination_postal_code": self.real_shipping_id.zip, - "courier_type": "reg", + "origin_note": "BELAKANG INDOMARET", + "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", "destination_postal_code": self.real_shipping_id.zip, @@ -421,11 +423,15 @@ class StockPicking(models.Model): # Cek jika pengiriman instant atau same_day if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "origin_note": "BELAKANG INDOMARET", - "courier_company": self.carrier_id.name.lower(), - "courier_type": self.sale_id.delivery_service_type, - "delivery_type": "now", - "items": items_data_instant # Gunakan items untuk instant + "origin_coordinate" :{ + "latitude": -6.3031123, + "longitude" : 106.7794934999 + }, + "destination_coordinate" : { + "latitude": self.real_shipping_id.latitude, + "longitude": self.real_shipping_id.longtitude, + }, + "items": items_data_instant }) headers = { @@ -440,8 +446,8 @@ class StockPicking(models.Model): data = response.json() self.biteship_id = data.get("id", "") - self.biteship_tracking_id = data.get("tracking_id", "") - self.biteship_waybill_id = data.get("waybill_id", "") + self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") + self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") return data else: -- cgit v1.2.3 From 92222d326692652b2c4146e0e6040c74f75d4abc Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 21 Feb 2025 11:29:14 +0700 Subject: tracking --- indoteknik_api/controllers/api_v1/stock_picking.py | 2 +- indoteknik_custom/models/stock_picking.py | 93 ++++++++++++++++++++-- 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 110cde8a..2fc4d8a5 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -101,7 +101,7 @@ class StockPicking(controller.Controller): picking = picking_model.browse(id) if not picking: return self.response(None) - + hostori = picking.get_tracking_detail() return self.response(picking.get_tracking_detail()) @http.route(prefix + 'stock-picking//tracking', auth='public', method=['GET', 'OPTIONS']) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index e7d9dbd5..49c17788 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -12,8 +12,15 @@ import base64 import requests import time import logging +import re +from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) +_biteship_url = "https://api.biteship.com/v1" +_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" + + + class StockPicking(models.Model): _inherit = 'stock.picking' # check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) @@ -363,9 +370,10 @@ class StockPicking(models.Model): raise UserError(f"Kesalahan tidak terduga: {str(e)}") def action_send_to_biteship(self): - url = "https://api.biteship.com/v1/orders" - api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" - + + if self.biteship_tracking_id: + raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") + # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) @@ -401,6 +409,7 @@ class StockPicking(models.Model): }) payload = { + "reference_id " : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -433,14 +442,15 @@ class StockPicking(models.Model): }, "items": items_data_instant }) - + + api_key = _biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } # Kirim request ke Biteship - response = requests.post(url, headers=headers, json=payload) + response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) if response.status_code == 200: data = response.json() @@ -448,6 +458,7 @@ class StockPicking(models.Model): self.biteship_id = data.get("id", "") self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") + self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") return data else: @@ -1016,8 +1027,19 @@ class StockPicking(models.Model): 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, 'eta': self.generate_eta_delivery(), + 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } + + if self.biteship_id : + histori = self.get_manifest_biteship() + response['manifests'] = histori.get("manifests", []) + response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False + response['status'] = self._map_status_biteship(histori.get("delivered")) + + response + + return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False @@ -1030,6 +1052,67 @@ class StockPicking(models.Model): return response + def get_manifest_biteship(self): + api_key = _biteship_api_key + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + + manifests = [] + + try: + # Kirim request ke Biteship + response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) + result = response.json() + if(result.get('success') == True): + history = result.get("history", []) + status = result.get("status", "") + + for entry in reversed(history): + manifests.append({ + "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), + "datetime": self._convert_to_local_time(entry["updated_at"]), + "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + }) + + return { + "manifests": manifests, + "delivered": status + } + + return manifests + except Exception as e : + _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") + return { 'error': str(e) } + + def _convert_to_local_time(self, iso_date): + try: + dt_with_tz = waktu.fromisoformat(iso_date) + utc_dt = dt_with_tz.astimezone(pytz.utc) + + local_tz = pytz.timezone("Asia/Jakarta") + local_dt = utc_dt.astimezone(local_tz) + + return local_dt.strftime("%Y-%m-%d %H:%M:%S") + except Exception as e: + return str(e) + + def _map_status_biteship(self, status): + status_mapping = { + "confirmed": "pending", + "scheduled": "pending", + "allocated": "pending", + "picking_up": "pending", + "picked": "shipment", + "cancelled": "cancelled", + "on_hold": "on_hold", + "dropping_off": "shipment", + "delivered": "completed" + } + return status_mapping.get(status, "Hubungi Admin") + def generate_eta_delivery(self): current_date = datetime.datetime.now() prepare_days = 3 -- cgit v1.2.3 From f0d995cc220cefffe65ce308ee234528ddc0d6ed Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 09:33:28 +0700 Subject: biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 7 ++++++- indoteknik_custom/models/public_holiday.py | 4 ++-- indoteknik_custom/views/public_holiday.xml | 2 -- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2fc4d8a5..3e58417f 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -136,4 +136,9 @@ class StockPicking(controller.Controller): return self.response({ 'name': picking_data.name - }) \ No newline at end of file + }) + + @http.route(prefix + 'n', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + def udpate_status_from_bitehsip(self, **kw): + picking_code = int(kw.get('picking_code', 0)) + \ No newline at end of file diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py index 70b7c53a..851d9080 100644 --- a/indoteknik_custom/models/public_holiday.py +++ b/indoteknik_custom/models/public_holiday.py @@ -6,6 +6,6 @@ class PublicHoliday(models.Model): _description = 'Public Holidays' name = fields.Char(string='Holiday Name', required=True) - start_date = fields.Date('Start Holiday Date', required=True) - end_date = fields.Date('End Holiday Date', required=True) + start_date = fields.Date('Date Holiday', required=True) + # end_date = fields.Date('End Holiday Date', required=True) # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml index d0b9c5d5..146c5b0b 100644 --- a/indoteknik_custom/views/public_holiday.xml +++ b/indoteknik_custom/views/public_holiday.xml @@ -22,7 +22,6 @@ - @@ -36,7 +35,6 @@ - -- cgit v1.2.3 From a9c4cd0c5ac694074f0e3a4359182a97f27f542e Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 11:26:24 +0700 Subject: webhook biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 37 ++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 3e58417f..01269724 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -138,7 +138,40 @@ class StockPicking(controller.Controller): 'name': picking_data.name }) - @http.route(prefix + 'n', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def udpate_status_from_bitehsip(self, **kw): - picking_code = int(kw.get('picking_code', 0)) + try: + data = request.jsonrequest # Ambil data JSON dari request + event = data.get('event') + + # Log Webhook ke Model Odoo + request.env['webhook.logs'].sudo().create({ + 'event': event, + 'order_id': data.get('order_id'), + 'courier_tracking_id': data.get('courier_tracking_id'), + 'courier_waybill_id': data.get('courier_waybill_id'), + 'status': data.get('status'), + 'order_price': data.get('price'), + 'cash_on_delivery_fee': data.get('cash_on_delivery_fee'), + 'proof_of_delivery_fee': data.get('proof_of_delivery_fee'), + 'shippment_fee': data.get('shippment_fee'), + }) + + # Handle Event Berdasarkan Jenisnya + if event == "order.status": + self.process_order_status(data) + elif event == "order.price": + self.process_order_price(data) + elif event == "order.waybill_id": + self.process_order_waybill(data) + + return {'success': True, 'message': f'Webhook {event} received'} + except Exception as e: + return {'success': False, 'message': str(e)} + + def process_order_status(self, data): + """Update status order di Odoo""" + order = request.env['sale.order'].sudo().search([('id', '=', data.get('order_id'))], limit=1) + if order: + order.write({'state': data.get('status')}) \ No newline at end of file -- cgit v1.2.3 From 1d2011c7b1b9766b0254479733b2ec226e8201bd Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 24 Feb 2025 17:16:45 +0700 Subject: add public holiday --- indoteknik_api/controllers/api_v1/product.py | 15 ++------ indoteknik_api/controllers/api_v1/sale_order.py | 4 ++- indoteknik_custom/models/sale_order.py | 48 ++++++++++++++++++++----- indoteknik_custom/models/stock_picking.py | 22 ++++++------ indoteknik_custom/views/sale_order.xml | 2 ++ 5 files changed, 57 insertions(+), 34 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 93ef305c..557215ea 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -7,18 +7,7 @@ import logging import math import json -_logger = logging.getLogger(__name__) - -def get_days_until_next_business_day(start_date=None, *args, **kwargs): - today = start_date or datetime.today().date() - offset = 0 # Counter jumlah hari yang ditambahkan - - while today.weekday() >= 5 : - today += timedelta(days=1) - offset += 1 - - return offset - +_logger = logging.getLogger(__name__) class Product(controller.Controller): @@ -88,7 +77,7 @@ class Product(controller.Controller): break start_date = datetime.today().date() - additional_days = get_days_until_next_business_day(start_date) + additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) # Jika semua loop selesai tanpa include_instant menjadi False return self.response({ diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index 8b95ade8..4afeb21b 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -386,7 +386,8 @@ class SaleOrder(controller.Controller): 'note_website': [], 'voucher': [], 'source': [], - 'estimated_arrival_days': ['number', 'default:0'] + 'estimated_arrival_days': ['number', 'default:0'], + 'estimated_arrival_days_start': ['number', 'default:0'] }) if not params['valid']: @@ -416,6 +417,7 @@ class SaleOrder(controller.Controller): 'partner_purchase_order_file': params['value']['po_file'], 'delivery_amt': params['value']['delivery_amount'] * 1.10, 'estimated_arrival_days': params['value']['estimated_arrival_days'], + 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], 'shipping_cost_covered': 'customer', 'shipping_paid_by': 'customer', 'carrier_id': params['value']['carrier_id'], diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d0b57a3d..d956e93a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -93,11 +93,13 @@ class SaleOrder(models.Model): applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) amount_voucher_shipping_disc = fields.Float(string='Voucher Discount') source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True) - estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0) + estimated_arrival_days = fields.Integer('Estimated Arrival To', default=0) + estimated_arrival_days_start = fields.Integer('Estimated Arrival From', default=0) email = fields.Char(string='Email') 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') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_start_date') + eta_date = fields.Datetime(string='ETA Date end', copy=False, compute='_compute_eta_date') flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web') is_continue_transaction = fields.Boolean(string='Button Transaction', help='Data dari web') web_approval = fields.Selection([ @@ -146,9 +148,7 @@ class SaleOrder(models.Model): ]) estimated_ready_ship_date = fields.Datetime( string='ET Ready to Ship compute', - compute='_compute_etrts_date', - store=True - + compute='_compute_etrts_date' ) expected_ready_to_ship = fields.Datetime( string='ET Ready to Ship', @@ -156,7 +156,7 @@ class SaleOrder(models.Model): store=True ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') - + def _compute_shipping_method_picking(self): for order in self: if order.picking_ids: @@ -383,13 +383,43 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True - @api.depends('date_order', 'estimated_arrival_days', 'state') + @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): for rec in self: if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) else: rec.eta_date = False + rec.eta_date_start = False + + @api.depends('date_order', 'state', 'estimated_arrival_days_start') + def _compute_eta_start_date(self): + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days_start: + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) + else: + rec.eta_date_start = False + + def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + holiday = self.env['hr.public.holiday'] + + while True : + today += timedelta(days=1) + offset += 1 + + if today.weekday() >= 5: + continue + + is_holiday = holiday.search([("start_date", "=", today)]) + if is_holiday: + continue + + break + + return offset @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date @@ -401,7 +431,7 @@ class SaleOrder(models.Model): max_slatime = max(max_slatime, slatime) if rec.date_order: - eta_date = datetime.now() + timedelta(days=max_slatime) + eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) rec.estimated_ready_ship_date = eta_date rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default @@ -1470,7 +1500,7 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) - # order._compute_etrts_date() + order._compute_etrts_date() # order._update_partner_details() return order diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1690a4ed..be395cef 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -177,19 +177,19 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, store=True, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_ready_to_ship', store=True, default=False) + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', default=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - # @api.depends('estimated_ready_ship_date', 'state') - # def _compute_countdown_hours(self): - # for record in self: - # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # # Gunakan nilai yang sangat besar sebagai placeholder - # record.countdown_hours = 999999 - # else: - # delta = record.estimated_ready_ship_date - waktu.now() - # record.countdown_hours = delta.total_seconds() / 3600 + @api.depends('estimated_ready_ship_date', 'state') + def _compute_countdown_hours(self): + for record in self: + if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # Gunakan nilai yang sangat besar sebagai placeholder + record.countdown_hours = 999999 + else: + delta = record.estimated_ready_ship_date - waktu.now() + record.countdown_hours = delta.total_seconds() / 3600 @api.depends('estimated_ready_ship_date', 'state') def _compute_countdown_ready_to_ship(self): diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index b267eee4..4cc96cd2 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -68,6 +68,8 @@ + + -- cgit v1.2.3 From 49a90fdef07cb9262eb43e63c7023e30925a3c0c Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 25 Feb 2025 14:29:52 +0700 Subject: webhook biteship --- indoteknik_api/controllers/api_v1/stock_picking.py | 37 ++++++++++++---------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 9da9575b..15aac3cd 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -144,19 +144,6 @@ class StockPicking(controller.Controller): data = request.jsonrequest # Ambil data JSON dari request event = data.get('event') - # Log Webhook ke Model Odoo - request.env['webhook.logs'].sudo().create({ - 'event': event, - 'order_id': data.get('order_id'), - 'courier_tracking_id': data.get('courier_tracking_id'), - 'courier_waybill_id': data.get('courier_waybill_id'), - 'status': data.get('status'), - 'order_price': data.get('price'), - 'cash_on_delivery_fee': data.get('cash_on_delivery_fee'), - 'proof_of_delivery_fee': data.get('proof_of_delivery_fee'), - 'shippment_fee': data.get('shippment_fee'), - }) - # Handle Event Berdasarkan Jenisnya if event == "order.status": self.process_order_status(data) @@ -170,8 +157,26 @@ class StockPicking(controller.Controller): return {'success': False, 'message': str(e)} def process_order_status(self, data): - """Update status order di Odoo""" - order = request.env['sale.order'].sudo().search([('id', '=', data.get('order_id'))], limit=1) + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if data.get('status') == 'picked': + picking_model.write({'driver_departure_date': datetime.utcnow()}) + elif data.get('status') == 'delivered': + picking_model.write({'driver_arrival_date': datetime.utcnow()}) + + def process_order_price(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + order = request.env['sale.order'].sudo().search([('name', '=', picking_model.sale_id.name)], limit=1) if order: - order.write({'state': data.get('status')}) + order.write({ + 'delivery_amt': data.get('price') + }) + + def process_order_waybill(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if picking_model: + picking_model.write({ + 'biteship_waybill_id': data.get('courier_waybill_id'), + 'delivery_tracking_no': data.get('courier_waybill_id'), + 'biteship_tracking_id':data.get('courier_tracking_id') + }) \ No newline at end of file -- cgit v1.2.3 From 39da2566a2af32b3fdaeae1ce826e4f778e9b8ce Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Tue, 25 Feb 2025 14:30:39 +0700 Subject: ketinggalan --- indoteknik_custom/models/sale_order.py | 7 ------- indoteknik_custom/models/stock_picking.py | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d956e93a..43177f33 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -393,13 +393,6 @@ class SaleOrder(models.Model): rec.eta_date = False rec.eta_date_start = False - @api.depends('date_order', 'state', 'estimated_arrival_days_start') - def _compute_eta_start_date(self): - for rec in self: - if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days_start: - rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) - else: - rec.eta_date_start = False def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): today = start_date or datetime.today().date() diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index be395cef..00db6717 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1097,6 +1097,12 @@ class StockPicking(models.Model): def get_tracking_detail(self): self.ensure_one() + + order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) + + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start).strftime('%d %b') + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days).strftime('%d %b %Y') + formatted_eta = f"{eta_start} - {eta_end}" response = { 'delivery_order': { @@ -1109,7 +1115,7 @@ class StockPicking(models.Model): 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, - 'eta': self.generate_eta_delivery(), + 'eta': formatted_eta, 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } -- cgit v1.2.3 From 62caad158a936eee9a0b85fd4df0c664374b6bfb Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 26 Feb 2025 10:47:01 +0700 Subject: biteship tracking --- indoteknik_custom/models/sale_order.py | 2 +- indoteknik_custom/models/stock_picking.py | 25 +++++++++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 43177f33..634ea918 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -98,7 +98,7 @@ class SaleOrder(models.Model): email = fields.Char(string='Email') picking_iu_id = fields.Many2one('stock.picking', 'Picking IU') helper_by_id = fields.Many2one('res.users', 'Helper By') - eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_start_date') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_date') eta_date = fields.Datetime(string='ETA Date end', copy=False, compute='_compute_eta_date') flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web') is_continue_transaction = fields.Boolean(string='Button Transaction', help='Data dari web') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 00db6717..605452e3 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1099,15 +1099,12 @@ class StockPicking(models.Model): self.ensure_one() order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) - - eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start).strftime('%d %b') - eta_end = order.date_order + timedelta(days=order.estimated_arrival_days).strftime('%d %b %Y') - formatted_eta = f"{eta_start} - {eta_end}" response = { 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '', + 'service' : order.delivery_service_type or '', 'receiver_name': '', 'receiver_city': '' }, @@ -1115,19 +1112,21 @@ class StockPicking(models.Model): 'status': self.shipping_status, 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, - 'eta': formatted_eta, + 'eta': self.generate_eta_delivery(), 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } if self.biteship_id : histori = self.get_manifest_biteship() + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start) + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days) + formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" + response['eta'] = formatted_eta response['manifests'] = histori.get("manifests", []) response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False response['status'] = self._map_status_biteship(histori.get("delivered")) - response - return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: @@ -1155,6 +1154,15 @@ class StockPicking(models.Model): # Kirim request ke Biteship response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) result = response.json() + description = { + 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', + 'allocated' : 'Kurir akan melakukan pick-up pesanan', + 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("name", ""), + 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', + 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + 'delivered' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") + } if(result.get('success') == True): history = result.get("history", []) status = result.get("status", "") @@ -1163,7 +1171,8 @@ class StockPicking(models.Model): manifests.append({ "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), "datetime": self._convert_to_local_time(entry["updated_at"]), - "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + # "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + "description": description[entry["status"]], }) return { -- cgit v1.2.3 From ce7faa60bc86686c0fcabec9aebb7a35c8fd7395 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 27 Feb 2025 11:11:16 +0700 Subject: update source get vendor sla --- indoteknik_custom/models/vendor_sla.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 67b6ffc3..852baa7a 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -20,33 +20,36 @@ class VendorSLA(models.Model): # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration def generate_vendor_id_sla(self): - # Step 1: Group purchase orders by vendor (partner_id) - po_env = self.env['purchase.order'] - pos = po_env.read_group( - domain=[('state', '=', 'done')], - fields=['partner_id', 'date_planned', 'date_approve'], + # Step 1: Group stock pickings by vendor based on operation type + stock_picking_env = self.env['stock.picking'] + stock_moves = stock_picking_env.read_group( + domain=[ + ('state', '=', 'done'), + ('location_id', '=', 4), # Partner Locations/Vendors + ('location_dest_id', '=', 57) # BU/Stock + ], + fields=['partner_id', 'date_done', 'scheduled_date'], groupby=['partner_id'], lazy=False ) - for group in pos: + for group in stock_moves: partner_id = group['partner_id'][0] total_duration = 0 count = 0 # Step 2: Calculate the average duration for each vendor - pos_for_vendor = po_env.search([ + pos_for_vendor = stock_picking_env.search([ ('partner_id', '=', partner_id), ('state', '=', 'done'), - ('date_planned', '>=', '2023-01-01') ]) for po in pos_for_vendor: - if po.date_planned and po.date_approve: - date_planned = fields.Datetime.to_datetime(po.date_planned) - date_approve = fields.Datetime.to_datetime(po.date_approve) - if date_planned < date_approve: continue - duration = (date_planned - date_approve).total_seconds() / 3600 # Convert to hours + if po.date_done and po.purchase_id.date_approve: + date_of_transfer = fields.Datetime.to_datetime(po.date_done) + po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) + if date_of_transfer < po_confirmation_date: continue + duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours total_duration += duration count += 1 -- cgit v1.2.3 From d5e40546164b98fd9f819bc4f65f53d8b7c3c7f4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 28 Feb 2025 09:55:08 +0700 Subject: sequance --- indoteknik_custom/models/stock_picking.py | 105 +++++++++++++++++++++++------- indoteknik_custom/views/stock_picking.xml | 6 +- 2 files changed, 87 insertions(+), 24 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 605452e3..696d25db 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -23,6 +23,8 @@ _biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1l class StockPicking(models.Model): _inherit = 'stock.picking' + _order = 'final_seq ASC' + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') @@ -178,33 +180,92 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_compute_countdown_hours', default=False) - countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_compute_countdown_ready_to_ship') - - @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_hours(self): + countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + final_seq_tmp = fields.Float(string='Sequance Order in hours', store=True, compute_sudo=True) + final_seq = fields.Float(string='Sequance Order', related='final_seq_tmp', index=True) + + execution_date = fields.Float( + string='Time Remainder by date Reserved', + store=True, # Menyimpan hasil ke database + ) + def _compute_execution_date_by_date_reserved(self, date_reserved): for record in self: - if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: - # Gunakan nilai yang sangat besar sebagai placeholder - record.countdown_hours = 999999 - else: - delta = record.estimated_ready_ship_date - waktu.now() - record.countdown_hours = delta.total_seconds() / 3600 - + try: + if record.date_reserved and record.state not in ('cancel', 'done'): + date_reserved = record.date_reserved + timedelta(days=1) + time_diff = (date_reserved - waktu.now()).total_seconds() / 3600 + record.execution_date = time_diff + else: + record.execution_date = 99999999999 # Kosongkan jika tidak memenuhi kondisi + + except Exception as e: + error = f"Error calculating sequance {str(e)}" + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + return { 'error': str(e) } + + + # @api.depends('date_reserved') + # def _callculate_final_sequance(self): + # filtered_records = self.filtered(lambda r: r.estimated_ready_ship_date and r.date_reserved and r.state not in ('cancel', 'done')) + # for record in filtered_records: + # estimated_by_erts = (record.estimated_ready_ship_date - waktu.now()).total_seconds() / 3600 + # estimated_by_date = (record.date_reserved - waktu.now()).total_seconds() / 3600 + # record.final_seq_tmp = min(estimated_by_erts, estimated_by_date) + + # (self - filtered_records).write({'final_seq_tmp': 99999999999}) + + @api.depends('estimated_ready_ship_date', 'state') - def _compute_countdown_ready_to_ship(self): + def _callculate_sequance(self): for record in self: - if record.state in ('cancel', 'done'): - record.countdown_ready_to_ship = False - else: - if record.estimated_ready_ship_date: - delta = record.estimated_ready_ship_date - waktu.now() - days = delta.days - hours, remainder = divmod(delta.seconds, 3600) - record.countdown_ready_to_ship = f"{days} days, {hours} hours" - record.countdown_hours = delta.total_seconds() / 3600 + try : + if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + rts = record.estimated_ready_ship_date - waktu.now() + rts_days = rts.days + rts_hours = divmod(rts.seconds, 3600) + + estimated_by_erts = rts.total_seconds() / 3600 + + record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + record.countdown_hours = estimated_by_erts + record.final_seq = estimated_by_erts else: + + record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False + except Exception as e : + error = str(e) + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + + print(str(e)) + return { 'error': str(e) } + + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_ready_to_ship(self): + # for record in self: + # if record.state in ('cancel', 'done'): + # record.countdown_ready_to_ship = False + # else: + # if record.estimated_ready_ship_date: + # delta = record.estimated_ready_ship_date - waktu.now() + # days = delta.days + # hours, remainder = divmod(delta.seconds, 3600) + # record.countdown_ready_to_ship = f"{days} days, {hours} hours" + # record.countdown_hours = delta.total_seconds() / 3600 + # else: + # record.countdown_ready_to_ship = False def _compute_lalamove_image_html(self): for record in self: diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 1832c31e..61ee2610 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,8 +7,8 @@ - countdown_hours asc - + final_seq asc + @@ -18,7 +18,9 @@ + + -- cgit v1.2.3 From 5802838642db8bd0969d9bedc68606710f1ef4b5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 10:36:29 +0700 Subject: CR renca tempo --- indoteknik_api/controllers/api_v1/partner.py | 2 ++ indoteknik_custom/models/res_partner.py | 3 +++ indoteknik_custom/models/user_pengajuan_tempo.py | 2 ++ indoteknik_custom/models/user_pengajuan_tempo_request.py | 4 +++- indoteknik_custom/views/user_pengajuan_tempo.xml | 2 ++ 5 files changed, 12 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 307165b3..57d2fdf9 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -357,7 +357,9 @@ class Partner(controller.Controller): 'districtPengiriman': ['alias:district_id_pengiriman'], 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], 'zipPengiriman': ['alias:zip_pengiriman'], + 'PICBarangMobile': ['alias:pic_mobile'], 'invoicePicTittle': ['alias:invoice_pic_tittle'], + 'invoicePicMobile': ['alias:invoice_pic_mobile'], 'invoicePic': ['alias:invoice_pic'], 'streetInvoice': ['alias:street_invoice'], 'stateInvoice': ['alias:state_id_invoice'], diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 7e574a72..78380135 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -58,6 +58,7 @@ class ResPartner(models.Model): # Pengiriman pic_name = fields.Char(string='Nama PIC Penerimaan Barang') + pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang') street_pengiriman = fields.Char(string="Alamat Perusahaan") state_id_pengiriman = fields.Many2one('res.country.state', string='State') city_id_pengiriman = fields.Many2one('vit.kota', string='City') @@ -254,6 +255,7 @@ class ResPartner(models.Model): # Pengiriman vals['pic_name'] = vals.get('pic_name', self.pic_name) + vals['pic_mobile'] = vals.get('pic_mobile', self.pic_mobile) vals['street_pengiriman'] = vals.get('street_pengiriman', self.street_pengiriman) vals['state_id_pengiriman'] = vals.get('state_id_pengiriman', self.state_id_pengiriman) vals['city_id_pengiriman'] = vals.get('city_id_pengiriman', self.city_id_pengiriman) @@ -323,6 +325,7 @@ class ResPartner(models.Model): 'finance_mobile': vals.get('finance_mobile'), 'finance_email': vals.get('finance_email'), 'pic_name': vals.get('pic_name'), + 'pic_mobile': vals.get('pic_mobile'), 'street_pengiriman': vals.get('street_pengiriman'), 'state_id_pengiriman': vals.get('state_id_pengiriman'), 'city_id_pengiriman': vals.get('city_id_pengiriman'), diff --git a/indoteknik_custom/models/user_pengajuan_tempo.py b/indoteknik_custom/models/user_pengajuan_tempo.py index 0fdcdbeb..d10e7e81 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo.py +++ b/indoteknik_custom/models/user_pengajuan_tempo.py @@ -74,6 +74,7 @@ class UserPengajuanTempo(models.Model): # Pengiriman pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang') + pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang') pic_name = fields.Char(string='Nama PIC Penerimaan Barang') street_pengiriman = fields.Char(string="Alamat Perusahaan") state_id_pengiriman = fields.Many2one('res.country.state', string='State') @@ -83,6 +84,7 @@ class UserPengajuanTempo(models.Model): zip_pengiriman = fields.Char(string="Zip") invoice_pic_tittle = fields.Char(string='Tittle PIC Penerimaan Invoice') invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice') + invoice_pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice') street_invoice = fields.Char(string="Alamat Perusahaan") state_id_invoice = fields.Many2one('res.country.state', string='State') city_id_invoice = fields.Many2one('vit.kota', string='City') diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index be4293a0..8920d7c4 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -108,6 +108,7 @@ class UserPengajuanTempoRequest(models.Model): # Pengiriman pic_tittle = fields.Char(string='Tittle PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_tittle', store=True, readonly=False) + pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_mobile', store=True, readonly=False) pic_name = fields.Char(string='Nama PIC Penerimaan Barang', related='pengajuan_tempo_id.pic_name', store=True, readonly=False) street_pengiriman = fields.Char(string="Alamat Perusahaan", related='pengajuan_tempo_id.street_pengiriman', store=True, readonly=False) state_id_pengiriman = fields.Many2one('res.country.state', string='State', related='pengajuan_tempo_id.state_id_pengiriman', store=True, readonly=False) @@ -292,7 +293,7 @@ class UserPengajuanTempoRequest(models.Model): self.pengajuan_tempo_id.finance_mobile = self.finance_mobile self.pengajuan_tempo_id.finance_email = self.finance_email - @api.onchange('pic_tittle', 'pic_name', 'street_pengiriman', 'state_id_pengiriman', 'city_id_pengiriman', + @api.onchange('pic_tittle','pic_mobile', 'pic_name', 'street_pengiriman', 'state_id_pengiriman', 'city_id_pengiriman', 'zip_pengiriman', 'district_id_pengiriman', 'subDistrict_id_pengiriman' 'invoice_pic_tittle', 'invoice_pic', 'street_invoice', 'state_id_invoice', 'city_id_invoice', 'district_id_invoice', 'subDistrict_id_invoice', 'zip_invoice', @@ -302,6 +303,7 @@ class UserPengajuanTempoRequest(models.Model): if self.pengajuan_tempo_id: # Perbarui nilai di pengajuan_tempo_id self.pengajuan_tempo_id.pic_tittle = self.pic_tittle + self.pengajuan_tempo_id.pic_mobile = self.pic_mobile self.pengajuan_tempo_id.pic_name = self.pic_name self.pengajuan_tempo_id.street_pengiriman = self.street_pengiriman self.pengajuan_tempo_id.state_id_pengiriman = self.state_id_pengiriman diff --git a/indoteknik_custom/views/user_pengajuan_tempo.xml b/indoteknik_custom/views/user_pengajuan_tempo.xml index 7f1faa41..4eebe9e4 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo.xml @@ -53,6 +53,7 @@ + @@ -62,6 +63,7 @@ + -- cgit v1.2.3 From f53c699804806a83252901f5aa076c2f9ddcb8b4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:24:35 +0700 Subject: biteship --- indoteknik_custom/models/stock_picking.py | 39 ++----------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 85cdc7eb..95c94fad 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,41 +179,10 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.sale') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) - final_seq_tmp = fields.Float(string='Sequance Order in hours', store=True, compute_sudo=True) - final_seq = fields.Float(string='Sequance Order', related='final_seq_tmp', index=True) - - execution_date = fields.Float( - string='Time Remainder by date Reserved', - store=True, # Menyimpan hasil ke database - ) - def _compute_execution_date_by_date_reserved(self, date_reserved): - for record in self: - try: - if record.date_reserved and record.state not in ('cancel', 'done'): - date_reserved = record.date_reserved + timedelta(days=1) - time_diff = (date_reserved - waktu.now()).total_seconds() / 3600 - record.execution_date = time_diff - else: - record.execution_date = 99999999999 # Kosongkan jika tidak memenuhi kondisi - - except Exception as e: - error = f"Error calculating sequance {str(e)}" - _logger.error(f"Error calculating sequance {self.id}: {str(e)}") - return { 'error': str(e) } - - - # @api.depends('date_reserved') - # def _callculate_final_sequance(self): - # filtered_records = self.filtered(lambda r: r.estimated_ready_ship_date and r.date_reserved and r.state not in ('cancel', 'done')) - # for record in filtered_records: - # estimated_by_erts = (record.estimated_ready_ship_date - waktu.now()).total_seconds() / 3600 - # estimated_by_date = (record.date_reserved - waktu.now()).total_seconds() / 3600 - # record.final_seq_tmp = min(estimated_by_erts, estimated_by_date) - - # (self - filtered_records).write({'final_seq_tmp': 99999999999}) + final_seq = fields.Float(string='Sequance Order', index=True) @api.depends('estimated_ready_ship_date', 'state') @@ -229,15 +198,11 @@ class StockPicking(models.Model): record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" record.countdown_hours = estimated_by_erts - record.final_seq = estimated_by_erts else: - record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False except Exception as e : - error = str(e) _logger.error(f"Error calculating sequance {self.id}: {str(e)}") - print(str(e)) return { 'error': str(e) } -- cgit v1.2.3 From 8d577ade2d913506d9d3996894e7b839f850d36a Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:34:53 +0700 Subject: uodate key biteship --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 95c94fad..73aacdf4 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -17,7 +17,7 @@ from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) _biteship_url = "https://api.biteship.com/v1" -_biteship_api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" -- cgit v1.2.3 From e3b5d1c9bdd8a764983b01442ddf041e8ade87bf Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 13:51:50 +0700 Subject: fixing error --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 73aacdf4..bde7402f 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,7 +179,7 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.sale') + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Sequance Order', index=True) -- cgit v1.2.3 From 6b80ada3ebc5477b51666ccb41cf30184a1d1af0 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 14:01:45 +0700 Subject: udpate bugs --- indoteknik_custom/models/stock_picking.py | 5 +++++ indoteknik_custom/views/stock_picking.xml | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index bde7402f..17ef1228 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -183,6 +183,11 @@ class StockPicking(models.Model): countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Sequance Order', index=True) + + + def schduled_update_sequance(self): + query = "SELECT update_sequance_stock_picking();" + self.env.cr.execute(query) @api.depends('estimated_ready_ship_date', 'state') diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 61ee2610..9d19b97c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -18,7 +18,6 @@ - -- cgit v1.2.3 From 8a48cbf462ce04b5c4be6c7ff29d0193c92572e0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 14:12:45 +0700 Subject: update pengajuan tempo --- indoteknik_api/controllers/api_v1/partner.py | 20 +++++++++++++++++++- indoteknik_custom/models/res_partner.py | 3 +++ .../models/user_pengajuan_tempo_request.py | 4 +++- indoteknik_custom/views/res_partner.xml | 2 ++ .../views/user_pengajuan_tempo_request.xml | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 57d2fdf9..9bb015c4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -311,6 +311,7 @@ class Partner(controller.Controller): company_name = kw.get('name', pengajuan_tempo.name_tempo.name) partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) user_account = self.get_user_by_email(user.email) + dokumen_prosedur = kw.get('dokumen_prosedur', False) params = self.get_request_params(kw, { @@ -350,6 +351,7 @@ class Partner(controller.Controller): # Pengiriman 'PICTittle': ['alias:pic_tittle'], + 'PICBarangMobile': ['alias:pic_mobile'], 'PICName': ['alias:pic_name'], 'streetPengiriman': ['alias:street_pengiriman'], 'statePengiriman': ['alias:state_id_pengiriman'], @@ -357,7 +359,6 @@ class Partner(controller.Controller): 'districtPengiriman': ['alias:district_id_pengiriman'], 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], 'zipPengiriman': ['alias:zip_pengiriman'], - 'PICBarangMobile': ['alias:pic_mobile'], 'invoicePicTittle': ['alias:invoice_pic_tittle'], 'invoicePicMobile': ['alias:invoice_pic_mobile'], 'invoicePic': ['alias:invoice_pic'], @@ -402,6 +403,23 @@ class Partner(controller.Controller): if partner_id: pengajuan_tempo.name_tempo = partner_id + # Prosedur Pengiriman + if dokumen_prosedur: + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['details']['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(dokumen_prosedur['details']['base64']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': dokumen_prosedur['details']['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.pengajuan.tempo', + 'res_id': pengajuan_tempo.id, + 'mimetype': mimetype + }) + pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] + + form_supplier_data = kw.get('formSupplier', False) if form_supplier_data: diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 78380135..56c69f95 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -66,6 +66,7 @@ class ResPartner(models.Model): subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan') zip_pengiriman = fields.Char(string="Zip") invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice') + invoice_pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice') street_invoice = fields.Char(string="Alamat Perusahaan") state_id_invoice = fields.Many2one('res.country.state', string='State') city_id_invoice = fields.Many2one('vit.kota', string='City') @@ -263,6 +264,7 @@ class ResPartner(models.Model): vals['subDistrict_id_pengiriman'] = vals.get('subDistrict_id_pengiriman', self.subDistrict_id_pengiriman) vals['zip_pengiriman'] = vals.get('zip_pengiriman', self.zip_pengiriman) vals['invoice_pic'] = vals.get('invoice_pic', self.invoice_pic) + vals['invoice_pic_mobile'] = vals.get('invoice_pic_mobile', self.invoice_pic_mobile) vals['street_invoice'] = vals.get('street_invoice', self.street_invoice) vals['state_id_invoice'] = vals.get('state_id_invoice', self.state_id_invoice) vals['city_id_invoice'] = vals.get('city_id_invoice', self.city_id_invoice) @@ -333,6 +335,7 @@ class ResPartner(models.Model): 'subDistrict_id_pengiriman': vals.get('subDistrict_id_pengiriman'), 'zip_pengiriman': vals.get('zip_pengiriman'), 'invoice_pic': vals.get('invoice_pic'), + 'invoice_pic_mobile': vals.get('invoice_pic_mobile'), 'street_invoice': vals.get('street_invoice'), 'state_id_invoice': vals.get('state_id_invoice'), 'city_id_invoice': vals.get('city_id_invoice'), diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 8920d7c4..72580001 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -117,6 +117,7 @@ class UserPengajuanTempoRequest(models.Model): subDistrict_id_pengiriman = fields.Many2one('vit.kelurahan', string='Kelurahan', related='pengajuan_tempo_id.subDistrict_id_pengiriman', store=True, readonly=False) zip_pengiriman = fields.Char(string="Zip", related='pengajuan_tempo_id.zip_pengiriman', store=True, readonly=False) invoice_pic_tittle = fields.Char(string='Tittle PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_tittle', store=True, readonly=False) + invoice_pic_mobile = fields.Char(string='Nomor HP PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic_mobile', store=True, readonly=False) invoice_pic = fields.Char(string='Nama PIC Penerimaan Invoice', related='pengajuan_tempo_id.invoice_pic', store=True, readonly=False) street_invoice = fields.Char(string="Alamat Perusahaan", related='pengajuan_tempo_id.street_invoice', store=True, readonly=False) state_id_invoice = fields.Many2one('res.country.state', string='State', related='pengajuan_tempo_id.state_id_invoice', store=True, readonly=False) @@ -295,7 +296,7 @@ class UserPengajuanTempoRequest(models.Model): @api.onchange('pic_tittle','pic_mobile', 'pic_name', 'street_pengiriman', 'state_id_pengiriman', 'city_id_pengiriman', 'zip_pengiriman', 'district_id_pengiriman', 'subDistrict_id_pengiriman' - 'invoice_pic_tittle', 'invoice_pic', 'street_invoice', 'state_id_invoice', 'city_id_invoice', + 'invoice_pic_tittle','invoice_pic_mobile', 'invoice_pic', 'street_invoice', 'state_id_invoice', 'city_id_invoice', 'district_id_invoice', 'subDistrict_id_invoice', 'zip_invoice', 'tukar_invoice', 'jadwal_bayar', 'dokumen_pengiriman', 'dokumen_pengiriman_input', 'dokumen_invoice', 'is_same_address', 'is_same_address_street') @@ -312,6 +313,7 @@ class UserPengajuanTempoRequest(models.Model): self.pengajuan_tempo_id.subDistrict_id_pengiriman = self.subDistrict_id_pengiriman self.pengajuan_tempo_id.zip_pengiriman = self.zip_pengiriman self.pengajuan_tempo_id.invoice_pic_tittle = self.invoice_pic_tittle + self.pengajuan_tempo_id.invoice_pic_mobile = self.invoice_pic_mobile self.pengajuan_tempo_id.invoice_pic = self.invoice_pic self.pengajuan_tempo_id.street_invoice = self.street_invoice self.pengajuan_tempo_id.state_id_invoice = self.state_id_invoice diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index af5e0db3..b87e616d 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -131,6 +131,7 @@ + @@ -140,6 +141,7 @@ + diff --git a/indoteknik_custom/views/user_pengajuan_tempo_request.xml b/indoteknik_custom/views/user_pengajuan_tempo_request.xml index 7063231b..3ab00ed9 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo_request.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo_request.xml @@ -102,6 +102,7 @@ + @@ -111,6 +112,7 @@ + -- cgit v1.2.3 From 59f5be7f2145530979dcb0d0ff23197a4aa0c589 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 5 Mar 2025 14:31:32 +0700 Subject: push --- indoteknik_api/controllers/api_v1/product.py | 81 ++++++-- indoteknik_api/controllers/api_v1/sale_order.py | 4 +- indoteknik_api/controllers/api_v1/stock_picking.py | 47 ++++- indoteknik_custom/__manifest__.py | 2 + indoteknik_custom/models/__init__.py | 2 + indoteknik_custom/models/product_sla.py | 133 ++++++++----- indoteknik_custom/models/product_template.py | 12 ++ indoteknik_custom/models/public_holiday.py | 11 ++ indoteknik_custom/models/sale_order.py | 101 ++++++++-- indoteknik_custom/models/stock_picking.py | 212 +++++++++++++++++++-- indoteknik_custom/models/vendor_sla.py | 98 ++++++++++ indoteknik_custom/views/product_product.xml | 8 + indoteknik_custom/views/product_sla.xml | 7 +- indoteknik_custom/views/public_holiday.xml | 55 ++++++ indoteknik_custom/views/sale_order.xml | 3 + indoteknik_custom/views/stock_picking.xml | 6 +- indoteknik_custom/views/vendor_sla.xml | 42 ++++ indoteknik_custom/views/x_banner_category.xml | 2 +- 18 files changed, 727 insertions(+), 99 deletions(-) create mode 100644 indoteknik_custom/models/public_holiday.py create mode 100644 indoteknik_custom/models/vendor_sla.py create mode 100644 indoteknik_custom/views/public_holiday.xml create mode 100644 indoteknik_custom/views/vendor_sla.xml diff --git a/indoteknik_api/controllers/api_v1/product.py b/indoteknik_api/controllers/api_v1/product.py index 32362582..557215ea 100644 --- a/indoteknik_api/controllers/api_v1/product.py +++ b/indoteknik_api/controllers/api_v1/product.py @@ -1,13 +1,13 @@ from .. import controller from odoo import http -from odoo.http import request +from odoo.http import request, Response from datetime import datetime, timedelta import ast import logging import math import json -_logger = logging.getLogger(__name__) +_logger = logging.getLogger(__name__) class Product(controller.Controller): @@ -33,9 +33,64 @@ class Product(controller.Controller): categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) - - @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) + + @http.route(prefix + 'product/variants/sla', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() + def get_product_template_sla_by_id(self, **kwargs): + body_params = kwargs.get('ids') + + if not body_params: + return self.response('Failed', code=400, description='id is required') + + ids = [int(id.strip()) for id in body_params.split(',') if id.strip().isdigit()] + + sla_duration = 0 + sla_unit = 'Hari' + include_instant = True + products = request.env['product.product'].search([('id', 'in', ids)]) + if len(products) < 1: + return self.response( + 'Failed', + code=400, + description='Produk Tidak Di Temukan.' + ) + + product_slas = request.env['product.sla'].search([('product_variant_id', 'in', ids)]) + if len(product_slas) < 1: + return self.response( + 'Failed', + code=400, + description='SLA Tidak Di Temukan.' + ) + + # Mapping SLA untuk mempermudah lookup + sla_map = {sla.product_variant_id.id: sla for sla in product_slas} + + for product in products: + product_sla = sla_map.get(product.id) + if product_sla: + sla_duration = max(sla_duration, int(product_sla.sla)) + sla_unit = product_sla.sla_vendor_id.unit + if product.qty_free_bandengan < 1 : + if product_sla.sla_vendor_id.unit != 'jam': + include_instant = False + break + + start_date = datetime.today().date() + additional_days = request.env['sale.order'].get_days_until_next_business_day(start_date) + + # Jika semua loop selesai tanpa include_instant menjadi False + return self.response({ + 'include_instant': include_instant, + 'sla_duration': sla_duration, + 'sla_additional_days': additional_days, + 'sla_total' : int(sla_duration) + int(additional_days), + 'sla_unit': 'Hari' if additional_days > 0 else sla_unit + } + ) + + @http.route(prefix + 'product_variant//stock', auth='public', methods=['GET', 'OPTIONS']) + @controller.Controller.must_authorized() def get_product_template_stock_by_id(self, **kw): id = int(kw.get('id')) date_7_days_ago = datetime.now() - timedelta(days=7) @@ -49,10 +104,11 @@ class Product(controller.Controller): ], limit=1) qty_available = product.qty_free_bandengan - - if qty_available < 0: - qty_available = 0 - + + + if qty_available < 1 : + qty_available = 0 + qty = 0 sla_date = '-' @@ -74,24 +130,25 @@ class Product(controller.Controller): if qty_available > 0: qty = qty_available + total_adem + total_excell + sla_date = product_sla.sla or 1 elif qty_altama > 0 or qty_vendor > 0: qty = total_adem if qty_altama > 0 else total_excell - sla_date = '2-4 Hari' + sla_date = product_sla.sla else: - sla_date = '3-7 Hari' + sla_date = product_sla.sla except: print('error') else: if qty_available > 0: qty = qty_available - sla_date = product_sla.sla or '-' + sla_date = product_sla.sla or 'Indent' elif qty_vendor > 0: qty = total_excell sla_date = '2-4 Hari' data = { 'qty': qty, - 'sla_date': sla_date, + 'sla_date': sla_date } return self.response(data, headers=[('Cache-Control', 'max-age=600, private')]) diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index a7e027c8..6815bf6c 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -386,7 +386,8 @@ class SaleOrder(controller.Controller): 'note_website': [], 'voucher': [], 'source': [], - 'estimated_arrival_days': ['number', 'default:0'] + 'estimated_arrival_days': ['number', 'default:0'], + 'estimated_arrival_days_start': ['number', 'default:0'] }) if not params['valid']: @@ -417,6 +418,7 @@ class SaleOrder(controller.Controller): 'partner_purchase_order_file': params['value']['po_file'], 'delivery_amt': params['value']['delivery_amount'] * 1.10, 'estimated_arrival_days': params['value']['estimated_arrival_days'], + 'estimated_arrival_days_start': params['value']['estimated_arrival_days_start'], 'shipping_cost_covered': 'customer', 'shipping_paid_by': 'customer', 'carrier_id': params['value']['carrier_id'], diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 2e0c4ad0..15aac3cd 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -101,7 +101,7 @@ class StockPicking(controller.Controller): picking = picking_model.browse(id) if not picking: return self.response(None) - + hostori = picking.get_tracking_detail() return self.response(picking.get_tracking_detail()) @http.route(prefix + 'stock-picking//tracking', auth='public', method=['GET', 'OPTIONS']) @@ -136,4 +136,47 @@ class StockPicking(controller.Controller): return self.response({ 'name': picking_data.name - }) \ No newline at end of file + }) + + @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) + def udpate_status_from_bitehsip(self, **kw): + try: + data = request.jsonrequest # Ambil data JSON dari request + event = data.get('event') + + # Handle Event Berdasarkan Jenisnya + if event == "order.status": + self.process_order_status(data) + elif event == "order.price": + self.process_order_price(data) + elif event == "order.waybill_id": + self.process_order_waybill(data) + + return {'success': True, 'message': f'Webhook {event} received'} + except Exception as e: + return {'success': False, 'message': str(e)} + + def process_order_status(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if data.get('status') == 'picked': + picking_model.write({'driver_departure_date': datetime.utcnow()}) + elif data.get('status') == 'delivered': + picking_model.write({'driver_arrival_date': datetime.utcnow()}) + + def process_order_price(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + order = request.env['sale.order'].sudo().search([('name', '=', picking_model.sale_id.name)], limit=1) + if order: + order.write({ + 'delivery_amt': data.get('price') + }) + + def process_order_waybill(self, data): + picking_model = request.env['stock.picking'].sudo().search([('biteship_id', '=', data.get('order_id'))], limit=1) + if picking_model: + picking_model.write({ + 'biteship_waybill_id': data.get('courier_waybill_id'), + 'delivery_tracking_no': data.get('courier_waybill_id'), + 'biteship_tracking_id':data.get('courier_tracking_id') + }) + \ No newline at end of file diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py index f66314fa..a7096346 100755 --- a/indoteknik_custom/__manifest__.py +++ b/indoteknik_custom/__manifest__.py @@ -161,7 +161,9 @@ 'report/report_invoice.xml', 'report/report_picking.xml', 'report/report_sale_order.xml', + 'views/vendor_sla.xml', 'views/coretax_faktur.xml', + 'views/public_holiday.xml', 'views/stock_inventory.xml', ], 'demo': [], diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index 3573eddd..37a49332 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -139,8 +139,10 @@ from . import user_pengajuan_tempo from . import approval_retur_picking from . import va_multi_approve from . import va_multi_reject +from . import vendor_sla from . import stock_immediate_transfer from . import coretax_fatur +from . import public_holiday from . import ir_actions_report from . import barcoding_product from . import account_payment_register diff --git a/indoteknik_custom/models/product_sla.py b/indoteknik_custom/models/product_sla.py index 2e663d30..04ad2ffd 100644 --- a/indoteknik_custom/models/product_sla.py +++ b/indoteknik_custom/models/product_sla.py @@ -12,73 +12,110 @@ class ProductSla(models.Model): _rec_name = 'product_variant_id' product_variant_id = fields.Many2one('product.product',string='Product') - avg_leadtime = fields.Char(string='AVG Leadtime', readonly=True) - leadtime = fields.Char(string='Leadtime', readonly=True) + sla_vendor_id = fields.Many2one('vendor.sla',string='Vendor', readonly=True) + sla_vendor_duration = fields.Char(string='AVG Leadtime', related='sla_vendor_id.duration_unit') + sla_logistic = fields.Char(string='SLA Logistic', readonly=True) + sla_logistic_unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Logistic Time" + ) + sla_logistic_duration_unit = fields.Char(string="SLA Logistic Duration (Unit)") sla = fields.Char(string='SLA', readonly=True) version = fields.Integer(string="Version", compute="_compute_version") def _compute_version(self): for sla in self: sla.version = sla.product_variant_id.sla_version + + - def generate_product_variant_id_sla(self, limit=5000): - # Filter produk non-Altama + def generate_product_variant_id_sla(self, limit=500): + offset = 0 + # while True: products = self.env['product.product'].search([ - ('x_manufacture', 'not in', [10, 122, 89]), - ('location_id', '=', 57), - ('stock_move_ids', '!=', False), - ], order='sla_version asc', limit=limit) + ('active', '=', True), + ('sale_ok', '=', True), + ], order='sla_version asc', limit=limit, offset=offset) + + # if not products: + # break - i = 1 for product in products: - _logger.info(f'Product SLA: {i}/{len(products)}') - i += 1 - product.sla_version += 1 + _logger.info(f'Memproses SLA untuk produk ID {product.id}, versi {product.sla_version}') product_sla = self.search([('product_variant_id', '=', product.id)], limit=1) if not product_sla: - product_sla = self.env['product.sla'].create({ - 'product_variant_id': product.id, - }) - + product_sla = self.create({'product_variant_id': product.id}) + product_sla.generate_product_sla() + # Tandai produk sebagai sudah diproses + product.sla_version += 1 + + offset += limit + + def generate_product_sla(self): - self.avg_leadtime = '-' - self.sla = '-' + # self.sla_logistic = '-' + # self.sla_logistic_duration_unit = '-' + # self.sla = '-' product = self.product_variant_id - - qty_available = 0 - qty_available = product.qty_onhand_bandengan + + q_vendor = [ + ('product_id', '=', product.id), + ('is_winner', '=', True) + ] + + vendor = self.env['purchase.pricelist'].search(q_vendor) + vendor_duration = 0 - if qty_available > 0: - self.sla = '1 Hari' + #SLA Vendor + if vendor: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + sla_vendor = int(vendor_sla.duration) if vendor_sla else 0 + if sla_vendor > 0: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration * 60 + + self.sla_vendor_id = vendor_sla.id if vendor_sla else False + #SLA Logistik selalu 1 hari + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + self.sla = math.ceil(estimation_sla_days) + self.sla_logistic = int(1) + self.sla_logistic_unit = 'hari' + self.sla_logistic_duration_unit = '1 hari' + else: + self.unlink() + else: + self.unlink() + - query = [ - ('product_id', '=', product.id), - ('picking_id', '!=', False), - ('picking_id.location_id', '=', 57), - ('picking_id.state', 'not in', ['cancel']) - ] - picking = self.env['stock.move.line'].search(query) - leadtimes=[] - for stock in picking: - date_delivered = stock.picking_id.driver_departure_date - date_so_confirmed = stock.picking_id.sale_id.date_order - if date_delivered and date_so_confirmed: - leadtime = date_delivered - date_so_confirmed - leadtime_in_days = leadtime.days - leadtimes.append(leadtime_in_days) + # query = [ + # ('product_id', '=', product.id), + # ('picking_id', '!=', False), + # ('picking_id.location_id', '=', 57), + # ('picking_id.state', 'not in', ['cancel']) + # ] + # picking = self.env['stock.move.line'].search(query) + + # leadtimes=[] + # for stock in picking: + # date_delivered = stock.picking_id.driver_departure_date + # date_do_ready = stock.picking_id.date_reserved + # if date_delivered and date_do_ready: + # leadtime = date_delivered - date_do_ready + # leadtime_in_days = leadtime.days + # leadtimes.append(leadtime_in_days) - if len(leadtimes) > 0: - avg_leadtime = sum(leadtimes) / len(leadtimes) - rounded_leadtime = math.ceil(avg_leadtime) - self.avg_leadtime = rounded_leadtime - if rounded_leadtime >= 1 and rounded_leadtime <= 5: - self.sla = '3-7 Hari' - elif rounded_leadtime >= 6 and rounded_leadtime <= 10: - self.sla = '4-12 Hari' - elif rounded_leadtime >= 11: - self.sla = 'Indent' \ No newline at end of file + # if len(leadtimes) > 0: + # avg_leadtime = sum(leadtimes) / len(leadtimes) + # rounded_leadtime = math.ceil(avg_leadtime) + # estimation_sla = ((rounded_leadtime * 24 * 60) + vendor_duration)/2 + # estimation_sla_days = estimation_sla / (24 * 60) + # self.sla = math.ceil(estimation_sla_days) + # self.avg_leadtime = int(rounded_leadtime) \ No newline at end of file diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index efacb95f..600dd90e 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -422,6 +422,18 @@ class ProductProduct(models.Model): merchandise_ok = fields.Boolean(string='Product Promotion') qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant') + def generate_product_sla(self): + product_variant_ids = self.env.context.get('active_ids', []) + product_variant = self.search([('id', 'in', product_variant_ids)]) + sla_record = self.env['product.sla'].search([('product_variant_id', '=', product_variant.id)], limit=1) + + if sla_record: + sla_record.generate_product_sla() + else: + new_sla_record = self.env['product.sla'].create({ + 'product_variant_id': product_variant.id, + }) + new_sla_record.generate_product_sla() @api.model def create(self, vals): group_id = self.env.ref('indoteknik_custom.group_role_merchandiser').id diff --git a/indoteknik_custom/models/public_holiday.py b/indoteknik_custom/models/public_holiday.py new file mode 100644 index 00000000..851d9080 --- /dev/null +++ b/indoteknik_custom/models/public_holiday.py @@ -0,0 +1,11 @@ +from odoo import api, fields, models +from datetime import timedelta, datetime + +class PublicHoliday(models.Model): + _name = 'hr.public.holiday' + _description = 'Public Holidays' + + name = fields.Char(string='Holiday Name', required=True) + start_date = fields.Date('Date Holiday', required=True) + # end_date = fields.Date('End Holiday Date', required=True) + # company_id = fields.Many2one('res.company', 'Company') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 8a983479..037fff7b 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -137,11 +137,13 @@ class SaleOrder(models.Model): applied_voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Applied Voucher', copy=False) amount_voucher_shipping_disc = fields.Float(string='Voucher Discount') source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]", required=True) - estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0) + estimated_arrival_days = fields.Integer('Estimated Arrival To', default=0) + estimated_arrival_days_start = fields.Integer('Estimated Arrival From', default=0) email = fields.Char(string='Email') 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') + eta_date_start = fields.Datetime(string='ETA Date start', copy=False, compute='_compute_eta_date') + eta_date = fields.Datetime(string='ETA Date end', copy=False, compute='_compute_eta_date') flash_sale = fields.Boolean(string='Flash Sale', help='Data dari web') is_continue_transaction = fields.Boolean(string='Button Transaction', help='Data dari web') web_approval = fields.Selection([ @@ -188,7 +190,17 @@ class SaleOrder(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) + estimated_ready_ship_date = fields.Datetime( + string='ET Ready to Ship compute', + compute='_compute_etrts_date' + ) + expected_ready_to_ship = fields.Datetime( + string='ET Ready to Ship', + copy=False, + store=True + ) shipping_method_picking = fields.Char(string='Shipping Method Picking', compute='_compute_shipping_method_picking') + reason_cancel = fields.Selection([ ('harga_terlalu_mahal', 'Harga barang terlalu mahal'), ('harga_web_tidak_valid', 'Harga web tidak valid'), @@ -435,19 +447,75 @@ class SaleOrder(models.Model): rec.compute_fullfillment = True + @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): - max_leadtime = 0 + for rec in self: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) + rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) + else: + rec.eta_date = False + rec.eta_date_start = False + + + def get_days_until_next_business_day(self,start_date=None, *args, **kwargs): + today = start_date or datetime.today().date() + offset = 0 # Counter jumlah hari yang ditambahkan + holiday = self.env['hr.public.holiday'] + + while True : + today += timedelta(days=1) + offset += 1 + + if today.weekday() >= 5: + continue - for line in self.order_line: - leadtime = line.vendor_id.leadtime - max_leadtime = max(max_leadtime, leadtime) + is_holiday = holiday.search([("start_date", "=", today)]) + if is_holiday: + continue + + break + + return offset + @api.depends("order_line.product_id") + def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: - if rec.date_order and rec.state not in ['cancel', 'draft']: - eta_date = datetime.now() + timedelta(days=max_leadtime) - rec.eta_date = eta_date - else: - rec.eta_date = False + max_slatime = 1 # Default SLA jika tidak ada + for line in rec.order_line: + product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + max_slatime = max(max_slatime, slatime) + + if rec.date_order: + eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) + rec.estimated_ready_ship_date = eta_date + rec.commitment_date = eta_date + # Jika expected_ready_to_ship kosong, set nilai default + if not rec.expected_ready_to_ship: + rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship + def _onchange_expected_ready_ship_date(self): + for rec in self: + if rec.expected_ready_to_ship and rec.estimated_ready_ship_date: + # Hanya membandingkan tanggal saja, tanpa jam + expected_date = rec.expected_ready_to_ship.date() + estimated_date = rec.estimated_ready_ship_date.date() + + if expected_date < estimated_date: + rec.expected_ready_to_ship = rec.estimated_ready_ship_date + rec.commitment_date = rec.estimated_ready_ship_date + raise ValidationError( + "Tanggal 'Expected Ready to Ship' tidak boleh lebih kecil dari {}. Mohon pilih tanggal minimal {}." + .format(estimated_date.strftime('%d-%m-%Y'), estimated_date.strftime('%d-%m-%Y')) + ) + + def _set_etrts_date(self): + for order in self: + if order.state in ('done', 'cancel', 'sale'): + raise UserError(_("You cannot change the Estimated Ready To Ship Date on a done, sale or cancelled order.")) + # order.move_lines.write({'estimated_ready_ship_date': order.estimated_ready_ship_date}) def _prepare_invoice(self): """ @@ -642,15 +710,16 @@ class SaleOrder(models.Model): if line.product_id.type == 'product': line_no += 1 line.line_no = line_no + def write(self, vals): res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() if 'carrier_id' in vals: for picking in self.picking_ids: if picking.state == 'assigned': picking.carrier_id = self.carrier_id - + return res def calculate_so_status(self): @@ -1108,6 +1177,7 @@ class SaleOrder(models.Model): order._set_sppkp_npwp_contact() order.calculate_line_no() order.send_notif_to_salesperson() + order._compute_etrts_date() # order.order_line.get_reserved_from() res = super(SaleOrder, self).action_confirm() @@ -1503,13 +1573,14 @@ class SaleOrder(models.Model): def create(self, vals): # Ensure partner details are updated when a sale order is created order = super(SaleOrder, self).create(vals) + order._compute_etrts_date() # order._update_partner_details() return order def write(self, vals): # Call the super method to handle the write operation res = super(SaleOrder, self).write(vals) - + # self._compute_etrts_date() # Check if the update is coming from a save operation # if any(field in vals for field in ['sppkp', 'npwp', 'email', 'customer_type']): # self._update_partner_details() @@ -1544,4 +1615,6 @@ class SaleOrder(models.Model): raise UserError( "SO tidak dapat ditambahkan produk baru karena SO sudah menjadi sale order.") res = super(SaleOrder, self).write(vals) + if 'order_line' in vals: + self._compute_etrts_date() return res \ No newline at end of file diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 36d9f63d..17ef1228 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,7 +1,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero -from datetime import timedelta, datetime +from datetime import timedelta, datetime as waktu from itertools import groupby import pytz, requests, json, requests from dateutil import parser @@ -12,10 +12,19 @@ import base64 import requests import time import logging +import re +from deep_translator import GoogleTranslator _logger = logging.getLogger(__name__) +_biteship_url = "https://api.biteship.com/v1" +_biteship_api_key = "biteship_live.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTc0MTE1NTU4M30.pbFCai9QJv8iWhgdosf8ScVmEeP3e5blrn33CHe7Hgo" + + + class StockPicking(models.Model): _inherit = 'stock.picking' + _order = 'final_seq ASC' + check_product_lines = fields.One2many('check.product', 'picking_id', string='Check Product', auto_join=True) barcode_product_lines = fields.One2many('barcode.product', 'picking_id', string='Barcode Product', auto_join=True) is_internal_use = fields.Boolean('Internal Use', help='flag which is internal use or not') @@ -166,6 +175,68 @@ class StockPicking(models.Model): lalamove_image_url = fields.Char(string="Lalamove Image URL") lalamove_image_html = fields.Html(string="Lalamove Image", compute="_compute_lalamove_image_html") + # Biteship Section + biteship_id = fields.Char(string="Biteship Respon ID") + biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") + biteship_waybill_id = fields.Char(string="Biteship Waybill ID") + estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + final_seq = fields.Float(string='Sequance Order', index=True) + + + def schduled_update_sequance(self): + query = "SELECT update_sequance_stock_picking();" + self.env.cr.execute(query) + + + @api.depends('estimated_ready_ship_date', 'state') + def _callculate_sequance(self): + for record in self: + try : + if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + rts = record.estimated_ready_ship_date - waktu.now() + rts_days = rts.days + rts_hours = divmod(rts.seconds, 3600) + + estimated_by_erts = rts.total_seconds() / 3600 + + record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + record.countdown_hours = estimated_by_erts + else: + record.countdown_hours = 999999999999 + record.countdown_ready_to_ship = False + except Exception as e : + _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + print(str(e)) + return { 'error': str(e) } + + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_hours(self): + # for record in self: + # if record.state in ('cancel', 'done') or not record.estimated_ready_ship_date: + # # Gunakan nilai yang sangat besar sebagai placeholder + # record.countdown_hours = 999999 + # else: + # delta = record.estimated_ready_ship_date - waktu.now() + # record.countdown_hours = delta.total_seconds() / 3600 + + # @api.depends('estimated_ready_ship_date', 'state') + # def _compute_countdown_ready_to_ship(self): + # for record in self: + # if record.state in ('cancel', 'done'): + # record.countdown_ready_to_ship = False + # else: + # if record.estimated_ready_ship_date: + # delta = record.estimated_ready_ship_date - waktu.now() + # days = delta.days + # hours, remainder = divmod(delta.seconds, 3600) + # record.countdown_ready_to_ship = f"{days} days, {hours} hours" + # record.countdown_hours = delta.total_seconds() / 3600 + # else: + # record.countdown_ready_to_ship = False + def _compute_lalamove_image_html(self): for record in self: if record.lalamove_image_url: @@ -321,7 +392,7 @@ class StockPicking(models.Model): picking.tracking_by = self.env.user.id ata_at_str = data.get("ata_at") envio_ata = self._convert_to_datetime(data.get("ata_at")) - + picking.driver_arrival_date = envio_ata if data.get("status") != 'delivered': picking.driver_arrival_date = False @@ -332,12 +403,13 @@ class StockPicking(models.Model): raise UserError(f"Kesalahan tidak terduga: {str(e)}") def action_send_to_biteship(self): - url = "https://api.biteship.com/v1/orders" - api_key = "biteship_test.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiSW5kb3Rla25payIsInVzZXJJZCI6IjY3MTViYTJkYzVkMjdkMDAxMjRjODk2MiIsImlhdCI6MTcyOTQ5ODAwMX0.L6C73couP4-cgVEfhKI2g7eMCMo3YOFSRZhS-KSuHNA" - + + if self.biteship_tracking_id: + raise UserError(f"Order ini sudah dikirim ke Biteship. Dengan Tracking Id: {self.biteship_tracking_id}") + # Mencari data sale.order.line berdasarkan sale_id products = self.env['sale.order.line'].search([('order_id', '=', self.sale_id.id)]) - + # Fungsi untuk membangun items_data dari order lines def build_items_data(lines): return [{ @@ -370,6 +442,7 @@ class StockPicking(models.Model): }) payload = { + "reference_id " : self.sale_id.name, "shipper_contact_name": self.carrier_id.pic_name or '', "shipper_contact_phone": self.carrier_id.pic_phone or '', "shipper_organization": self.carrier_id.name, @@ -381,7 +454,8 @@ class StockPicking(models.Model): "destination_contact_phone": self.real_shipping_id.phone or self.real_shipping_id.mobile, "destination_address": self.real_shipping_id.street, "destination_postal_code": self.real_shipping_id.zip, - "courier_type": "reg", + "origin_note": "BELAKANG INDOMARET", + "courier_type": self.sale_id.delivery_service_type or "reg", "courier_company": self.carrier_id.name.lower(), "delivery_type": "now", "destination_postal_code": self.real_shipping_id.zip, @@ -389,27 +463,42 @@ class StockPicking(models.Model): } # Cek jika pengiriman instant atau same_day - if "instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type: + if self.sale_id.delivery_service_type and ("instant" in self.sale_id.delivery_service_type or "same_day" in self.sale_id.delivery_service_type): payload.update({ - "origin_note": "BELAKANG INDOMARET", - "courier_company": self.carrier_id.name.lower(), - "courier_type": self.sale_id.delivery_service_type, - "delivery_type": "now", - "items": items_data_instant # Gunakan items untuk instant + "origin_coordinate" :{ + "latitude": -6.3031123, + "longitude" : 106.7794934999 + }, + "destination_coordinate" : { + "latitude": self.real_shipping_id.latitude, + "longitude": self.real_shipping_id.longtitude, + }, + "items": items_data_instant }) - + + api_key = _biteship_api_key headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } # Kirim request ke Biteship - response = requests.post(url, headers=headers, json=payload) + response = requests.post(_biteship_url+'/orders', headers=headers, json=payload) + + if response.status_code == 200: + data = response.json() + + self.biteship_id = data.get("id", "") + self.biteship_tracking_id = data.get("courier", {}).get("tracking_id", "") + self.biteship_waybill_id = data.get("courier", {}).get("waybill_id", "") + self.delivery_tracking_no = data.get("courier", {}).get("waybill_id", "") - if response.status_code == 201: - return response.json() + return data else: - raise UserError(f"Error saat mengirim ke Biteship: {response.content}") + error_data = response.json() + error_message = error_data.get("error", "Unknown error") + error_code = error_data.get("code", "No code provided") + raise UserError(f"Error saat mengirim ke Biteship: {error_message} (Code: {error_code})") @api.constrains('driver_departure_date') def constrains_driver_departure_date(self): @@ -1039,11 +1128,14 @@ class StockPicking(models.Model): def get_tracking_detail(self): self.ensure_one() + + order = self.env['sale.order'].search([('name', '=', self.sale_id.name)], limit=1) response = { 'delivery_order': { 'name': self.name, 'carrier': self.carrier_id.name or '', + 'service' : order.delivery_service_type or '', 'receiver_name': '', 'receiver_city': '' }, @@ -1052,8 +1144,21 @@ class StockPicking(models.Model): 'waybill_number': self.delivery_tracking_no or '', 'delivery_status': None, 'eta': self.generate_eta_delivery(), + 'is_biteship': True if self.biteship_id else False, 'manifests': self.get_manifests() } + + if self.biteship_id : + histori = self.get_manifest_biteship() + eta_start = order.date_order + timedelta(days=order.estimated_arrival_days_start) + eta_end = order.date_order + timedelta(days=order.estimated_arrival_days) + formatted_eta = f"{eta_start.strftime('%d %b')} - {eta_end.strftime('%d %b %Y')}" + response['eta'] = formatted_eta + response['manifests'] = histori.get("manifests", []) + response['delivered'] = histori.get("delivered", False) or self.sj_return_date != False or self.driver_arrival_date != False + response['status'] = self._map_status_biteship(histori.get("delivered")) + + return response if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: response['delivered'] = self.sj_return_date != False or self.driver_arrival_date != False @@ -1066,6 +1171,77 @@ class StockPicking(models.Model): return response + def get_manifest_biteship(self): + api_key = _biteship_api_key + headers = { + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json" + } + + + manifests = [] + + try: + # Kirim request ke Biteship + response = requests.get(_biteship_url+'/trackings/'+self.biteship_tracking_id, headers=headers, json=manifests) + result = response.json() + description = { + 'confirmed' : 'Indoteknik telah melakukan permintaan pick-up', + 'allocated' : 'Kurir akan melakukan pick-up pesanan', + 'picking_up' : 'Kurir sedang dalam perjalanan menuju lokasi pick-up', + 'picked' : 'Pesanan sudah di pick-up kurir '+result.get("courier", {}).get("name", ""), + 'on_hold' : 'Pesanan ditahan sementara karena masalah pengiriman', + 'dropping_off' : 'Kurir sudah ditugaskan dan pesanan akan segera diantar ke pembeli', + 'delivered' : 'Pesanan telah sampai dan diterima oleh '+result.get("destination", {}).get("contact_name", "") + } + if(result.get('success') == True): + history = result.get("history", []) + status = result.get("status", "") + + for entry in reversed(history): + manifests.append({ + "status": re.sub(r'[^a-zA-Z0-9\s]', ' ', entry["status"]).lower().capitalize(), + "datetime": self._convert_to_local_time(entry["updated_at"]), + # "description": GoogleTranslator(source='auto', target='id').translate(entry["note"]), + "description": description[entry["status"]], + }) + + return { + "manifests": manifests, + "delivered": status + } + + return manifests + except Exception as e : + _logger.error(f"Error fetching Biteship order for picking {self.id}: {str(e)}") + return { 'error': str(e) } + + def _convert_to_local_time(self, iso_date): + try: + dt_with_tz = waktu.fromisoformat(iso_date) + utc_dt = dt_with_tz.astimezone(pytz.utc) + + local_tz = pytz.timezone("Asia/Jakarta") + local_dt = utc_dt.astimezone(local_tz) + + return local_dt.strftime("%Y-%m-%d %H:%M:%S") + except Exception as e: + return str(e) + + def _map_status_biteship(self, status): + status_mapping = { + "confirmed": "pending", + "scheduled": "pending", + "allocated": "pending", + "picking_up": "pending", + "picked": "shipment", + "cancelled": "cancelled", + "on_hold": "on_hold", + "dropping_off": "shipment", + "delivered": "completed" + } + return status_mapping.get(status, "Hubungi Admin") + def generate_eta_delivery(self): current_date = datetime.datetime.now() prepare_days = 3 diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py new file mode 100644 index 00000000..852baa7a --- /dev/null +++ b/indoteknik_custom/models/vendor_sla.py @@ -0,0 +1,98 @@ +from odoo import models, fields, api +import logging +import math +_logger = logging.getLogger(__name__) + +class VendorSLA(models.Model): + _name = 'vendor.sla' + _description = 'Vendor SLA' + _rec_name = 'id_vendor' + + id_vendor = fields.Many2one('res.partner', string='Name', domain="[('industry_id', '=', 46)]") + duration = fields.Integer(string='Duration', description='SLA Duration') + unit = fields.Selection( + [('jam', 'Jam'),('hari', 'Hari')], + string="SLA Time" + ) + duration_unit = fields.Char(string="Duration (Unit)", compute="_compute_duration_unit") + + # pertama, lakukan group by vendor pada modul purchase.order + # kedua, pada setiap Purchase order pada group by vendor tersebut, lakukan penghitungan penjumlahan setiap nilai datetime field date_planed dikurangi date_approve purchase order + # dibagi jumlah data dari setiap Purchase order pada group by vendor tersebut. hasilnya lalu di gunakan untuk mengset nilai duration + def generate_vendor_id_sla(self): + # Step 1: Group stock pickings by vendor based on operation type + stock_picking_env = self.env['stock.picking'] + stock_moves = stock_picking_env.read_group( + domain=[ + ('state', '=', 'done'), + ('location_id', '=', 4), # Partner Locations/Vendors + ('location_dest_id', '=', 57) # BU/Stock + ], + fields=['partner_id', 'date_done', 'scheduled_date'], + groupby=['partner_id'], + lazy=False + ) + + for group in stock_moves: + partner_id = group['partner_id'][0] + total_duration = 0 + count = 0 + + # Step 2: Calculate the average duration for each vendor + pos_for_vendor = stock_picking_env.search([ + ('partner_id', '=', partner_id), + ('state', '=', 'done'), + ]) + + for po in pos_for_vendor: + if po.date_done and po.purchase_id.date_approve: + date_of_transfer = fields.Datetime.to_datetime(po.date_done) + po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) + if date_of_transfer < po_confirmation_date: continue + duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours + total_duration += duration + count += 1 + + if count > 0: + average_duration = total_duration / count + + # Step 3: Update the duration field in the corresponding res.partner record + vendor_sla = self.search([('id_vendor', '=', partner_id)], limit=1) + + # Konversi jam ke hari jika diperlukan + if average_duration >= 24: + days = average_duration / 24 + if days - int(days) > 0.5: # Jika sisa lebih dari 0,5, bulatkan ke atas + days = int(days) + 1 + else: # Jika sisa 0,5 atau kurang, bulatkan ke bawah + days = int(days) + duration_to_save = days + unit_to_save = 'hari' + else: + duration_to_save = round(average_duration) + unit_to_save = 'jam' + + # Update atau create vendor SLA record + if vendor_sla: + vendor_sla.write({ + 'duration': duration_to_save, + 'unit': unit_to_save + }) + else: + self.create({ + 'id_vendor': partner_id, + 'duration': duration_to_save, + 'unit': unit_to_save + }) + _logger.info(f'Proses SLA untuk Vendor selesai dilakukan') + + @api.depends('duration', 'unit') + def _compute_duration_unit(self): + for record in self: + if record.duration and record.unit: + record.duration_unit = f"{record.duration} {record.unit}" + else: + record.duration_unit = "-" + + + \ No newline at end of file diff --git a/indoteknik_custom/views/product_product.xml b/indoteknik_custom/views/product_product.xml index 71748e44..b214dc87 100644 --- a/indoteknik_custom/views/product_product.xml +++ b/indoteknik_custom/views/product_product.xml @@ -31,6 +31,14 @@ model.action_sync_to_solr() + + Generate Product SLA + + + code + model.generate_product_sla() + + Sync Variant To Solr: Solr Flag 2 diff --git a/indoteknik_custom/views/product_sla.xml b/indoteknik_custom/views/product_sla.xml index 8b0e874b..9179730f 100644 --- a/indoteknik_custom/views/product_sla.xml +++ b/indoteknik_custom/views/product_sla.xml @@ -6,7 +6,9 @@ - + + + @@ -21,7 +23,8 @@ - + + diff --git a/indoteknik_custom/views/public_holiday.xml b/indoteknik_custom/views/public_holiday.xml new file mode 100644 index 00000000..146c5b0b --- /dev/null +++ b/indoteknik_custom/views/public_holiday.xml @@ -0,0 +1,55 @@ + + + + + + hr.public.holiday access + + + + + + + + + + + hr.public.holiday.form + hr.public.holiday + +
+ + + + + + +
+
+
+ + + hr.public.holiday.tree + hr.public.holiday + + + + + + + + + Public Holidays + hr.public.holiday + tree,form + + + +
+
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 163330c5..ebee64b1 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -68,7 +68,10 @@ + + + diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 50ea40bf..9d19b97c 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -7,7 +7,8 @@ - create_date desc + final_seq asc + @@ -18,6 +19,9 @@ + + + diff --git a/indoteknik_custom/views/vendor_sla.xml b/indoteknik_custom/views/vendor_sla.xml new file mode 100644 index 00000000..cf4425eb --- /dev/null +++ b/indoteknik_custom/views/vendor_sla.xml @@ -0,0 +1,42 @@ + + + + Vendor SLA + vendor.sla + tree,form + + + + Vendor SLA + vendor.sla + + + + + + + + + + Vendor SLA + vendor.sla + +
+ + + + + + + +
+
+
+ + +
\ No newline at end of file diff --git a/indoteknik_custom/views/x_banner_category.xml b/indoteknik_custom/views/x_banner_category.xml index 11feb207..a83c4129 100755 --- a/indoteknik_custom/views/x_banner_category.xml +++ b/indoteknik_custom/views/x_banner_category.xml @@ -23,7 +23,7 @@ - + -- cgit v1.2.3 From 502d44ac6ecc8bbeeea35a205f326d0c56980934 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 5 Mar 2025 14:43:09 +0700 Subject: update code --- indoteknik_api/models/res_partner.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/indoteknik_api/models/res_partner.py b/indoteknik_api/models/res_partner.py index 0e09fbc6..3a98f3bc 100644 --- a/indoteknik_api/models/res_partner.py +++ b/indoteknik_api/models/res_partner.py @@ -59,6 +59,7 @@ class ResPartner(models.Model): # Pengiriman 'PIC_tittle' : pengajuan_tempo.pic_tittle if pengajuan_tempo.pic_tittle else '', + 'PICBarangMobile' : pengajuan_tempo.pic_mobile if pengajuan_tempo.pic_mobile else '', 'PIC_name' : pengajuan_tempo.pic_name if pengajuan_tempo.pic_name else '', 'street_pengiriman' : pengajuan_tempo.street_pengiriman if pengajuan_tempo.street_pengiriman else '', 'state_pengiriman' : pengajuan_tempo.state_id_pengiriman.id if pengajuan_tempo.state_id_pengiriman else '', @@ -67,6 +68,7 @@ class ResPartner(models.Model): 'subDistrict_pengiriman': pengajuan_tempo.subDistrict_id_pengiriman.id if pengajuan_tempo.subDistrict_id_pengiriman else '', 'zip_pengiriman' : pengajuan_tempo.zip_pengiriman if pengajuan_tempo.zip_pengiriman else '', 'invoice_pic_tittle' : pengajuan_tempo.invoice_pic_tittle if pengajuan_tempo.invoice_pic_tittle else '', + 'invoice_pic_mobile' : pengajuan_tempo.invoice_pic_mobile if pengajuan_tempo.invoice_pic_mobile else '', 'invoice_pic' : pengajuan_tempo.invoice_pic if pengajuan_tempo.invoice_pic else '', 'street_invoice' : pengajuan_tempo.street_invoice if pengajuan_tempo.street_invoice else '', 'state_invoice' : pengajuan_tempo.state_id_invoice.id if pengajuan_tempo.state_id_invoice else '', @@ -82,6 +84,12 @@ class ResPartner(models.Model): 'dokumen_pengiriman_invoice' : pengajuan_tempo.dokumen_invoice if pengajuan_tempo.dokumen_invoice else '', 'is_same_addrees': pengajuan_tempo.is_same_address if pengajuan_tempo.is_same_address else False, 'is_same_addrees_street': pengajuan_tempo.is_same_address_street if pengajuan_tempo.is_same_address_street else False, + 'dokumen_prosedur': + { + 'name': pengajuan_tempo.dokumen_prosedur.name, + 'base64': pengajuan_tempo.dokumen_prosedur.datas.decode('utf-8'), + 'format': pengajuan_tempo.dokumen_prosedur.mimetype, + } if pengajuan_tempo.dokumen_prosedur else '', 'supplier_ids': [ { 'id': supplier.id, -- cgit v1.2.3 From 0554da8389d1622dc91aafee74390de888db9a6f Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 22:03:13 +0700 Subject: update stock picing --- indoteknik_custom/models/stock_picking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 17ef1228..89531b79 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -182,7 +182,7 @@ class StockPicking(models.Model): estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) - final_seq = fields.Float(string='Sequance Order', index=True) + final_seq = fields.Float(string='Remaining Time') def schduled_update_sequance(self): @@ -207,7 +207,7 @@ class StockPicking(models.Model): record.countdown_hours = 999999999999 record.countdown_ready_to_ship = False except Exception as e : - _logger.error(f"Error calculating sequance {self.id}: {str(e)}") + _logger.error(f"Error calculating sequance {record.id}: {str(e)}") print(str(e)) return { 'error': str(e) } -- cgit v1.2.3 From 4115129907c5525c4688f2e6a3c28e0f249025b2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Wed, 5 Mar 2025 22:07:00 +0700 Subject: bug fix --- indoteknik_custom/models/stock_picking.py | 4 ++-- indoteknik_custom/views/stock_picking.xml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 89531b79..6e003d24 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -180,8 +180,8 @@ class StockPicking(models.Model): biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') - countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) - countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) + # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) + # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 9d19b97c..dadd5021 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -20,8 +20,8 @@ - - + -- cgit v1.2.3 From 222fbfd3f88b6acea279887f7a4aee249960f000 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:37:26 +0700 Subject: fixing bug prosuct sla indent --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 037fff7b..fc99da36 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,7 +484,7 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - slatime = int(product_sla.sla) if product_sla and product_sla.sla else 1 + slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 max_slatime = max(max_slatime, slatime) if rec.date_order: -- cgit v1.2.3 From afa847523195b695ec73430f9d1bda5a2fbfda51 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:47:25 +0700 Subject: validasi product sla --- indoteknik_custom/models/sale_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index fc99da36..e1074f42 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,6 +484,8 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + if(product_sla == False): + continue slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 max_slatime = max(max_slatime, slatime) -- cgit v1.2.3 From d516d0a28fdf78d0135cd480537e2079e1f1a006 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 08:53:40 +0700 Subject: tambah validasi --- indoteknik_custom/models/sale_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e1074f42..4304d889 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -486,7 +486,7 @@ class SaleOrder(models.Model): product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) if(product_sla == False): continue - slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' else 15 + slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 max_slatime = max(max_slatime, slatime) if rec.date_order: -- cgit v1.2.3 From 1abd80916c1ec843343dacf2cd3a359651e7cd43 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 09:20:32 +0700 Subject: comment relate field --- indoteknik_custom/models/stock_picking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 6e003d24..217f234e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -179,7 +179,7 @@ class StockPicking(models.Model): biteship_id = fields.Char(string="Biteship Respon ID") biteship_tracking_id = fields.Char(string="Biteship Trackcking ID") biteship_waybill_id = fields.Char(string="Biteship Waybill ID") - estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') + # estimated_ready_ship_date = fields.Datetime(string='ET Ready to Ship', copy=False, related='sale_id.estimated_ready_ship_date') # countdown_hours = fields.Float(string='Countdown in Hours', compute='_callculate_sequance', default=False, store=False, compute_sudo=False) # countdown_ready_to_ship = fields.Char(string='Countdown Ready to Ship', compute='_callculate_sequance', store=False, compute_sudo=False) final_seq = fields.Float(string='Remaining Time') -- cgit v1.2.3 From 3696d94c52234025390ef92ef8928bee34d0de5b Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 11:34:45 +0700 Subject: update code --- indoteknik_api/controllers/api_v1/partner.py | 9 +++++---- indoteknik_custom/models/user_pengajuan_tempo.py | 1 + .../models/user_pengajuan_tempo_request.py | 21 ++++++++++++++++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 9bb015c4..1d19f03d 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -311,7 +311,7 @@ class Partner(controller.Controller): company_name = kw.get('name', pengajuan_tempo.name_tempo.name) partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) user_account = self.get_user_by_email(user.email) - dokumen_prosedur = kw.get('dokumen_prosedur', False) + dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False params = self.get_request_params(kw, { @@ -405,11 +405,12 @@ class Partner(controller.Controller): # Prosedur Pengiriman if dokumen_prosedur: - mimetype, _ = mimetypes.guess_type(dokumen_prosedur['details']['name']) + dokumen_prosedur = json.loads(dokumen_prosedur) + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) mimetype = mimetype or 'application/octet-stream' - data = base64.b64decode(dokumen_prosedur['details']['base64']) + data = base64.b64decode(dokumen_prosedur['base64']) sppkp_attachment = request.env['ir.attachment'].create({ - 'name': dokumen_prosedur['details']['name'], + 'name': dokumen_prosedur['name'], 'type': 'binary', 'datas': base64.b64encode(data), 'res_model': 'user.pengajuan.tempo', diff --git a/indoteknik_custom/models/user_pengajuan_tempo.py b/indoteknik_custom/models/user_pengajuan_tempo.py index d10e7e81..0b3ab63d 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo.py +++ b/indoteknik_custom/models/user_pengajuan_tempo.py @@ -99,6 +99,7 @@ class UserPengajuanTempo(models.Model): dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice') is_same_address = fields.Boolean(string="Same Address pengiriman invoicr dan alamat pengiriman barang") is_same_address_street = fields.Boolean(string="Same Address pengiriman barang dan alamat bisnis") + dokumen_prosedur = fields.Many2many('ir.attachment', 'dokumen_prosedur_rel', string="Dokumen Prosedur", tracking=True) # Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line', string="Suppliers") diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py index 72580001..abcb6f2f 100644 --- a/indoteknik_custom/models/user_pengajuan_tempo_request.py +++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py @@ -132,7 +132,14 @@ class UserPengajuanTempoRequest(models.Model): dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice', related='pengajuan_tempo_id.dokumen_invoice', store=True, readonly=False) is_same_address = fields.Boolean(string="Same Address pengiriman invoicr dan alamat pengiriman barang", related='pengajuan_tempo_id.is_same_address', store=True, readonly=False) is_same_address_street = fields.Boolean(string="Same Address pengiriman barang dan alamat bisnis", related='pengajuan_tempo_id.is_same_address_street', store=True, readonly=False) - + dokumen_prosedur = fields.Many2many( + 'ir.attachment', + 'dokumen_prosedur_rel', + string="Dokumen Prosedur", + related='pengajuan_tempo_id.dokumen_prosedur', + readonly=False, + tracking=3 + ) #Referensi supplier_ids = fields.Many2many('user.pengajuan.tempo.line',related='pengajuan_tempo_id.supplier_ids', string="Suppliers", readonly=False) @@ -299,7 +306,7 @@ class UserPengajuanTempoRequest(models.Model): 'invoice_pic_tittle','invoice_pic_mobile', 'invoice_pic', 'street_invoice', 'state_id_invoice', 'city_id_invoice', 'district_id_invoice', 'subDistrict_id_invoice', 'zip_invoice', 'tukar_invoice', 'jadwal_bayar', 'dokumen_pengiriman', 'dokumen_pengiriman_input', 'dokumen_invoice', - 'is_same_address', 'is_same_address_street') + 'is_same_address', 'is_same_address_street','dokumen_prosedur') def _onchange_related_fields_pengiriman(self): if self.pengajuan_tempo_id: # Perbarui nilai di pengajuan_tempo_id @@ -328,6 +335,7 @@ class UserPengajuanTempoRequest(models.Model): self.pengajuan_tempo_id.dokumen_invoice = self.dokumen_invoice self.pengajuan_tempo_id.is_same_address = self.is_same_address self.pengajuan_tempo_id.is_same_address_street = self.is_same_address_street + self.pengajuan_tempo_id.dokumen_prosedur = self.dokumen_prosedur @api.onchange('supplier_ids') def _onchange_supplier_ids(self): @@ -341,7 +349,6 @@ class UserPengajuanTempoRequest(models.Model): def _onchange_related_fields_dokumen(self): if self.pengajuan_tempo_id: # Perbarui nilai di pengajuan_tempo_id - self.pengajuan_tempo_id.dokumen_nib = self.dokumen_nib self.pengajuan_tempo_id.dokumen_siup = self.dokumen_siup self.pengajuan_tempo_id.dokumen_tdp = self.dokumen_tdp self.pengajuan_tempo_id.dokumen_skdp = self.dokumen_skdp @@ -513,6 +520,7 @@ class UserPengajuanTempoRequest(models.Model): { "type": "delivery", "name": self.pengajuan_tempo_id.pic_name, + "phone": self.pengajuan_tempo_id.pic_mobile, "street": self.pengajuan_tempo_id.street_pengiriman, "state_id": self.pengajuan_tempo_id.state_id_pengiriman.id, "kota_id": self.pengajuan_tempo_id.city_id_pengiriman.id, @@ -523,6 +531,7 @@ class UserPengajuanTempoRequest(models.Model): { "type": "invoice", "name": self.pengajuan_tempo_id.invoice_pic, + "phone": self.pengajuan_tempo_id.invoice_pic_mobile, "street": self.pengajuan_tempo_id.street_invoice, "state_id": self.pengajuan_tempo_id.state_id_invoice.id, "kota_id": self.pengajuan_tempo_id.city_id_invoice.id, @@ -587,6 +596,10 @@ class UserPengajuanTempoRequest(models.Model): self.user_company_id.dokumen_pengiriman = self.pengajuan_tempo_id.dokumen_pengiriman self.user_company_id.dokumen_pengiriman_input = self.pengajuan_tempo_id.dokumen_pengiriman_input self.user_company_id.dokumen_invoice = self.pengajuan_tempo_id.dokumen_invoice + self.user_company_id.dokumen_prosedur = self.pengajuan_tempo_id.dokumen_prosedur[0] if self.pengajuan_tempo_id.dokumen_prosedur else [] + if self.user_company_id.dokumen_prosedur: + self.user_company_id.message_post(body='Dokumen Prosedur', + attachment_ids=[self.user_company_id.dokumen_prosedur.id]) # Referensi self.user_company_id.supplier_ids = self.pengajuan_tempo_id.supplier_ids @@ -596,6 +609,8 @@ class UserPengajuanTempoRequest(models.Model): if self.user_company_id.dokumen_npwp: self.user_company_id.message_post(body='Dokumen NPWP', attachment_ids=[self.user_company_id.dokumen_npwp.id]) + + self.user_company_id.dokumen_sppkp = self.pengajuan_tempo_id.dokumen_sppkp[0] if self.pengajuan_tempo_id.dokumen_sppkp else [] if self.user_company_id.dokumen_sppkp: self.user_company_id.message_post(body='Dokumen SPPKP', attachment_ids=[self.user_company_id.dokumen_sppkp.id]) -- cgit v1.2.3 From a4670aeed9688a33f56c07fe65984664390382f2 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 13:08:29 +0700 Subject: update code api edit address --- indoteknik_api/controllers/api_v1/partner.py | 68 +++++++++++++++------------- indoteknik_custom/models/sale_order.py | 4 +- 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 307165b3..d28aa8b4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -64,37 +64,43 @@ class Partner(controller.Controller): @http.route(prefix + 'partner//address', auth='public', methods=['PUT', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def write_partner_address_by_id(self, **kw): - params = self.get_request_params(kw, { - 'id': ['required', 'number'], - 'type': ['default:other'], - 'name': ['required'], - 'email': ['required'], - 'mobile': ['required'], - 'phone': [''], - 'street': ['required'], - 'state_id': ['required', 'number', 'alias:state_id'], - 'city_id': ['required', 'number', 'alias:kota_id'], - 'district_id': ['number', 'alias:kecamatan_id'], - 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], - 'zip': ['required'], - 'longtitude': [], - 'latitude': [], - 'address_map': [], - 'alamat_lengkap_text': [] - }) + try: + params = self.get_request_params(kw, { + 'id': ['required', 'number'], + 'type': ['default:other'], + 'name': ['required'], + 'email': ['required'], + 'mobile': ['required'], + 'phone': [''], + 'street': ['required'], + 'state_id': ['required', 'number', 'alias:state_id'], + 'city_id': ['required', 'number', 'alias:kota_id'], + 'district_id': ['number', 'alias:kecamatan_id'], + 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], + 'zip': ['required'], + 'longitude': '', # Perbaikan dari longtitude ke longitude + 'latitude': '', + 'address_map': [], + 'alamat_lengkap_text': [] + }) - if not params['valid']: - return self.response(code=400, description=params) - - partner = request.env[self._name].search([('id', '=', params['value']['id'])], limit=1) - if not partner: - return self.response(code=404, description='User not found') - - partner.write(params['value']) + if not params['valid']: + return self.response(code=400, description=params) - return self.response({ - 'id': partner.id - }) + partner = request.env[self._name].sudo().search([('id', '=', params['value']['id'])], limit=1) + + if not partner: + return self.response(code=404, description='User not found') + + try: + partner.write(params['value']) + except Exception as e: + return self.response(code=500, description=f'Error writing partner data: {str(e)}') + + return self.response({'id': partner.id}) + + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') @http.route(prefix + 'partner/address', auth='public', methods=['POST', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() @@ -111,8 +117,8 @@ class Partner(controller.Controller): 'city_id': ['required', 'number', 'alias:kota_id'], 'district_id': ['number', 'alias:kecamatan_id'], 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], - 'longtitude': [], - 'latitude': [], + 'longtitude': '', + 'latitude': '', 'address_map': [], 'zip': ['required'] }) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4304d889..4f85027a 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -484,8 +484,8 @@ class SaleOrder(models.Model): max_slatime = 1 # Default SLA jika tidak ada for line in rec.order_line: product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - if(product_sla == False): - continue + # if(product_sla == False): + # continue slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 max_slatime = max(max_slatime, slatime) -- cgit v1.2.3 From 405fbb57b55a2c01b1997e083b09e10fe838dbe2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 13:43:57 +0700 Subject: udpate webhook --- indoteknik_api/controllers/api_v1/stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/indoteknik_api/controllers/api_v1/stock_picking.py b/indoteknik_api/controllers/api_v1/stock_picking.py index 15aac3cd..55e07152 100644 --- a/indoteknik_api/controllers/api_v1/stock_picking.py +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -141,6 +141,9 @@ class StockPicking(controller.Controller): @http.route(prefix + 'webhook/biteship', type='json', auth='public', methods=['POST'], csrf=False) def udpate_status_from_bitehsip(self, **kw): try: + if not request.jsonrequest: + return "ok" + data = request.jsonrequest # Ambil data JSON dari request event = data.get('event') -- cgit v1.2.3 From e9e8fa207275f6dce9046324b18f6148977eba2e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 14:35:23 +0700 Subject: update --- indoteknik_api/controllers/api_v1/partner.py | 536 ++++++++++++++------------- 1 file changed, 272 insertions(+), 264 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 1d19f03d..74e2b0aa 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -303,291 +303,299 @@ class Partner(controller.Controller): @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST'], csrf=False) @controller.Controller.must_authorized() def write_pengajuan_tempo(self, **kw): - id = int(kw.get('partner_id')) - user_id = int(kw.get('user_id')) - tempo_request = True if kw.get('tempo_request') == 'true' else False - pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', user_id)], limit=1) - user = request.env['res.partner'].search([('id', '=', user_id)], limit=1) - company_name = kw.get('name', pengajuan_tempo.name_tempo.name) - partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) - user_account = self.get_user_by_email(user.email) - dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False - - params = self.get_request_params(kw, { - - # informasi perusahaan - # 'name': ['required', 'alias:name_tempo'], - 'industryId': ['alias:industry_id_tempo'], - 'street': ['alias:street_tempo'], - 'state': ['alias:state_id_tempo'], - 'city': ['alias:city_id_tempo'], - 'district': ['alias:district_id_tempo'], - 'subDistrict': ['alias:subDistrict_id_tempo'], - 'zip': ['alias:zip_tempo'], - 'mobile': ['alias:mobile_tempo'], - 'bankName': ['alias:bank_name_tempo'], - 'accountName': ['alias:account_name_tempo'], - 'accountNumber': ['alias:account_number_tempo'], - 'website': ['alias:website_tempo'], - 'estimasi': ['alias:estimasi_tempo'], - 'portal': ['alias:portal'], - 'bersedia': ['alias:bersedia'], - 'tempoDuration': ['alias:tempo_duration'], - 'tempoLimit': ['alias:tempo_limit'], - - # informasi perusahaan - 'direkturTittle': ['alias:direktur_tittle'], - 'direkturName': ['alias:direktur_name'], - 'direkturMobile': ['alias:direktur_mobile'], - 'direkturEmail': ['alias:direktur_email'], - 'purchasingTittle': ['alias:purchasing_tittle'], - 'purchasingName': ['alias:purchasing_name'], - 'purchasingMobile': ['alias:purchasing_mobile'], - 'purchasingEmail': ['alias:purchasing_email'], - 'financeTittle': ['alias:finance_tittle'], - 'financeName': ['alias:finance_name'], - 'financeMobile': ['alias:finance_mobile'], - 'financeEmail': ['alias:finance_email'], - - # Pengiriman - 'PICTittle': ['alias:pic_tittle'], - 'PICBarangMobile': ['alias:pic_mobile'], - 'PICName': ['alias:pic_name'], - 'streetPengiriman': ['alias:street_pengiriman'], - 'statePengiriman': ['alias:state_id_pengiriman'], - 'cityPengiriman': ['alias:city_id_pengiriman'], - 'districtPengiriman': ['alias:district_id_pengiriman'], - 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], - 'zipPengiriman': ['alias:zip_pengiriman'], - 'invoicePicTittle': ['alias:invoice_pic_tittle'], - 'invoicePicMobile': ['alias:invoice_pic_mobile'], - 'invoicePic': ['alias:invoice_pic'], - 'streetInvoice': ['alias:street_invoice'], - 'stateInvoice': ['alias:state_id_invoice'], - 'cityInvoice': ['alias:city_id_invoice'], - 'districtInvoice': ['alias:district_id_invoice'], - 'subDistrictInvoice': ['alias:subDistrict_id_invoice'], - 'zipInvoice': ['alias:zip_invoice'], - 'isSameAddrees':['alias:is_same_address'], - 'isSameAddreesStreet':['alias:is_same_address_street'], - }) - - # # Konversi nilai 'true' ke boolean True - # is_same_address = kw.get('isSameAddrees', 'false').lower() == 'true' - # is_same_address_street = kw.get('isSameAddreesStreet', 'false').lower() == 'true' - # - # # Tambahkan nilai yang dikonversi ke params - # if 'isSameAddress' in kw: - # params['value']['is_same_address'] = is_same_address - # if 'is_same_address_street' in kw: - # params['value']['is_same_address_street'] = is_same_address_street + try: + id = int(kw.get('partner_id')) + user_id = int(kw.get('user_id')) + tempo_request = True if kw.get('tempo_request') == 'true' else False + pengajuan_tempo = request.env['user.pengajuan.tempo'].search([('name_tempo', '=', user_id)], limit=1) + user = request.env['res.partner'].search([('id', '=', user_id)], limit=1) + company_name = kw.get('name', pengajuan_tempo.name_tempo.name) + partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) + user_account = self.get_user_by_email(user.email) + dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False + + params = self.get_request_params(kw, { + + # informasi perusahaan + # 'name': ['required', 'alias:name_tempo'], + 'industryId': ['alias:industry_id_tempo'], + 'street': ['alias:street_tempo'], + 'state': ['alias:state_id_tempo'], + 'city': ['alias:city_id_tempo'], + 'district': ['alias:district_id_tempo'], + 'subDistrict': ['alias:subDistrict_id_tempo'], + 'zip': ['alias:zip_tempo'], + 'mobile': ['alias:mobile_tempo'], + 'bankName': ['alias:bank_name_tempo'], + 'accountName': ['alias:account_name_tempo'], + 'accountNumber': ['alias:account_number_tempo'], + 'website': ['alias:website_tempo'], + 'estimasi': ['alias:estimasi_tempo'], + 'portal': ['alias:portal'], + 'bersedia': ['alias:bersedia'], + 'tempoDuration': ['alias:tempo_duration'], + 'tempoLimit': ['alias:tempo_limit'], + + # informasi perusahaan + 'direkturTittle': ['alias:direktur_tittle'], + 'direkturName': ['alias:direktur_name'], + 'direkturMobile': ['alias:direktur_mobile'], + 'direkturEmail': ['alias:direktur_email'], + 'purchasingTittle': ['alias:purchasing_tittle'], + 'purchasingName': ['alias:purchasing_name'], + 'purchasingMobile': ['alias:purchasing_mobile'], + 'purchasingEmail': ['alias:purchasing_email'], + 'financeTittle': ['alias:finance_tittle'], + 'financeName': ['alias:finance_name'], + 'financeMobile': ['alias:finance_mobile'], + 'financeEmail': ['alias:finance_email'], + + # Pengiriman + 'PICTittle': ['alias:pic_tittle'], + 'PICBarangMobile': ['alias:pic_mobile'], + 'PICName': ['alias:pic_name'], + 'streetPengiriman': ['alias:street_pengiriman'], + 'statePengiriman': ['alias:state_id_pengiriman'], + 'cityPengiriman': ['alias:city_id_pengiriman'], + 'districtPengiriman': ['alias:district_id_pengiriman'], + 'subDistrictPengiriman': ['alias:subDistrict_id_pengiriman'], + 'zipPengiriman': ['alias:zip_pengiriman'], + 'invoicePicTittle': ['alias:invoice_pic_tittle'], + 'invoicePicMobile': ['alias:invoice_pic_mobile'], + 'invoicePic': ['alias:invoice_pic'], + 'streetInvoice': ['alias:street_invoice'], + 'stateInvoice': ['alias:state_id_invoice'], + 'cityInvoice': ['alias:city_id_invoice'], + 'districtInvoice': ['alias:district_id_invoice'], + 'subDistrictInvoice': ['alias:subDistrict_id_invoice'], + 'zipInvoice': ['alias:zip_invoice'], + 'isSameAddrees':['alias:is_same_address'], + 'isSameAddreesStreet':['alias:is_same_address_street'], + }) - if not params['valid']: - return self.response(code=400, description=params) - if params['value']['portal']: - if params['value']['portal'] == 'ada': - params['value']['portal'] = True + # # Konversi nilai 'true' ke boolean True + # is_same_address = kw.get('isSameAddrees', 'false').lower() == 'true' + # is_same_address_street = kw.get('isSameAddreesStreet', 'false').lower() == 'true' + # + # # Tambahkan nilai yang dikonversi ke params + # if 'isSameAddress' in kw: + # params['value']['is_same_address'] = is_same_address + # if 'is_same_address_street' in kw: + # params['value']['is_same_address_street'] = is_same_address_street + + if not params['valid']: + return self.response(code=400, description=params) + if params['value']['portal']: + if params['value']['portal'] == 'ada': + params['value']['portal'] = True + else: + params['value']['portal'] = False + # Filter data baru yang dikirim (non-kosong, boolean False tetap masuk) + new_data = {key: value for key, value in params['value'].items() if value != ''} + + if pengajuan_tempo: + try: + pengajuan_tempo.write(new_data) + except Exception as e: + return self.response(code=500, description=f'Error updating partner data: {str(e)}') else: - params['value']['portal'] = False - # Filter data baru yang dikirim (non-kosong, boolean False tetap masuk) - new_data = {key: value for key, value in params['value'].items() if value != ''} - - if pengajuan_tempo: - # Jika pengajuan_tempo sudah ada, hanya write data baru yang non-kosong - pengajuan_tempo.write(new_data) - else: - # Jika belum ada, buat record baru - pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data) - pengajuan_tempo.partner_id = user_id - - if partner_id: - pengajuan_tempo.name_tempo = partner_id - - # Prosedur Pengiriman - if dokumen_prosedur: - dokumen_prosedur = json.loads(dokumen_prosedur) - mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) - mimetype = mimetype or 'application/octet-stream' - data = base64.b64decode(dokumen_prosedur['base64']) - sppkp_attachment = request.env['ir.attachment'].create({ - 'name': dokumen_prosedur['name'], - 'type': 'binary', - 'datas': base64.b64encode(data), - 'res_model': 'user.pengajuan.tempo', - 'res_id': pengajuan_tempo.id, - 'mimetype': mimetype - }) - pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) - pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] - - - form_supplier_data = kw.get('formSupplier', False) - - if form_supplier_data: - try: - form_supplier_data = json.loads(form_supplier_data) - - supplier_ids_to_add = [] - for item in form_supplier_data: - supplier_name = item.get("supplier") - pic_name = item.get("pic") - phone = item.get("telepon") - tempo_duration = item.get("durasiTempo") - credit_limit = item.get("creditLimit") - - new_data = { - 'name_supplier': supplier_name, - 'pic_name': pic_name, - 'phone': phone, - 'tempo_duration': tempo_duration, - 'credit_limit': credit_limit, - } - new_supplier_data = request.env['user.pengajuan.tempo.line'].create(new_data) - - supplier_ids_to_add.append(new_supplier_data.id) - - pengajuan_tempo.write({'supplier_ids': [(6, 0, supplier_ids_to_add)]}) - - except json.JSONDecodeError: - return http.Response(status=400, json_body={'error': 'Invalid JSON format for formSupplier'}) - category_produk_ids = kw.get('categoryProduk', False) - category_ids = '' - if category_produk_ids: - category_ids = list(map(int, category_produk_ids.split(','))) - pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] - - tukar_invoice_input = kw.get('tukarInvoiceInput') - if tukar_invoice_input: - pengajuan_tempo.tukar_invoice = tukar_invoice_input - - tukar_invoice_input_pembayaran = kw.get('tukarInvoiceInputPembayaran') - if tukar_invoice_input_pembayaran: - pengajuan_tempo.jadwal_bayar = tukar_invoice_input_pembayaran - - dokumen_kirim = [ - 'Surat Tanda Terima Barang (STTB)', - 'Good Receipt (GR)', - 'Surat Terima Barang (STB)', - 'Lembar Penerimaan Barang (LPB)' - ] - - dokumen_kirim_barang_ids = kw.get('dokumenPengiriman') - dokumen_kirim_input = kw.get('dokumenKirimInput', '') - dokumen_kirim_barang_input = kw.get('dokumenPengirimanInput', '') - dokumen_kirim_barang = [] - - if dokumen_kirim_barang_ids: - dokumen_kirim_ids = list(map(int, dokumen_kirim_barang_ids.split(','))) - dokumen_kirim_barang = [dokumen_kirim[i] for i in dokumen_kirim_ids if 0 <= i < len(dokumen_kirim)] - if dokumen_kirim_input: - input_items = [item.strip() for item in dokumen_kirim_input.split(',')] - dokumen_kirim_barang.extend(item for item in input_items if item and item not in dokumen_kirim_barang) - pengajuan_tempo.dokumen_kirim_input = dokumen_kirim_input - if dokumen_kirim_barang: - pengajuan_tempo.dokumen_pengiriman = ', '.join(dokumen_kirim_barang) - if dokumen_kirim_barang_input: - pengajuan_tempo.dokumen_pengiriman_input = dokumen_kirim_barang_input - - dokumen = [ - 'Invoice Pembelian', - 'Surat Jalan', - 'Berita Acara Serah Terima (BAST)', - 'Faktur Pajak', - 'Good Receipt (GR)' - ] - - dokumen_invoice = kw.get('dokumenPengirimanInvoice', '') - if dokumen_invoice: - pengajuan_tempo.dokumen_invoice = dokumen_invoice - user_tempo_request = [] - if tempo_request: - user_tempo_request = request.env['user.pengajuan.tempo.request'].create({ - 'user_id': id, - 'pengajuan_tempo_id': pengajuan_tempo.id, - 'user_company_id': partner_id.id, - 'tempo_duration': pengajuan_tempo.tempo_duration.id, - 'tempo_limit': pengajuan_tempo.tempo_limit, - }) + try: + pengajuan_tempo = request.env['user.pengajuan.tempo'].create(new_data) + pengajuan_tempo.partner_id = user_id + except Exception as e: + return self.response(code=500, description=f'Error creating partner data: {str(e)}') + + if partner_id: + pengajuan_tempo.name_tempo = partner_id + + # Prosedur Pengiriman + if dokumen_prosedur: + dokumen_prosedur = json.loads(dokumen_prosedur) + mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(dokumen_prosedur['base64']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': dokumen_prosedur['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.pengajuan.tempo', + 'res_id': pengajuan_tempo.id, + 'mimetype': mimetype + }) + pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] + + + form_supplier_data = kw.get('formSupplier', False) + + if form_supplier_data: + try: + form_supplier_data = json.loads(form_supplier_data) + + supplier_ids_to_add = [] + for item in form_supplier_data: + supplier_name = item.get("supplier") + pic_name = item.get("pic") + phone = item.get("telepon") + tempo_duration = item.get("durasiTempo") + credit_limit = item.get("creditLimit") + + new_data = { + 'name_supplier': supplier_name, + 'pic_name': pic_name, + 'phone': phone, + 'tempo_duration': tempo_duration, + 'credit_limit': credit_limit, + } + new_supplier_data = request.env['user.pengajuan.tempo.line'].create(new_data) + + supplier_ids_to_add.append(new_supplier_data.id) + + pengajuan_tempo.write({'supplier_ids': [(6, 0, supplier_ids_to_add)]}) + + except json.JSONDecodeError: + return http.Response(status=400, json_body={'error': 'Invalid JSON format for formSupplier'}) + category_produk_ids = kw.get('categoryProduk', False) + category_ids = '' + if category_produk_ids: + category_ids = list(map(int, category_produk_ids.split(','))) + pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] + + tukar_invoice_input = kw.get('tukarInvoiceInput') + if tukar_invoice_input: + pengajuan_tempo.tukar_invoice = tukar_invoice_input + + tukar_invoice_input_pembayaran = kw.get('tukarInvoiceInputPembayaran') + if tukar_invoice_input_pembayaran: + pengajuan_tempo.jadwal_bayar = tukar_invoice_input_pembayaran + + dokumen_kirim = [ + 'Surat Tanda Terima Barang (STTB)', + 'Good Receipt (GR)', + 'Surat Terima Barang (STB)', + 'Lembar Penerimaan Barang (LPB)' + ] + + dokumen_kirim_barang_ids = kw.get('dokumenPengiriman') + dokumen_kirim_input = kw.get('dokumenKirimInput', '') + dokumen_kirim_barang_input = kw.get('dokumenPengirimanInput', '') + dokumen_kirim_barang = [] + + if dokumen_kirim_barang_ids: + dokumen_kirim_ids = list(map(int, dokumen_kirim_barang_ids.split(','))) + dokumen_kirim_barang = [dokumen_kirim[i] for i in dokumen_kirim_ids if 0 <= i < len(dokumen_kirim)] + if dokumen_kirim_input: + input_items = [item.strip() for item in dokumen_kirim_input.split(',')] + dokumen_kirim_barang.extend(item for item in input_items if item and item not in dokumen_kirim_barang) + pengajuan_tempo.dokumen_kirim_input = dokumen_kirim_input + if dokumen_kirim_barang: + pengajuan_tempo.dokumen_pengiriman = ', '.join(dokumen_kirim_barang) + if dokumen_kirim_barang_input: + pengajuan_tempo.dokumen_pengiriman_input = dokumen_kirim_barang_input + + dokumen = [ + 'Invoice Pembelian', + 'Surat Jalan', + 'Berita Acara Serah Terima (BAST)', + 'Faktur Pajak', + 'Good Receipt (GR)' + ] + + dokumen_invoice = kw.get('dokumenPengirimanInvoice', '') + if dokumen_invoice: + pengajuan_tempo.dokumen_invoice = dokumen_invoice + user_tempo_request = [] + if tempo_request: + user_tempo_request = request.env['user.pengajuan.tempo.request'].create({ + 'user_id': id, + 'pengajuan_tempo_id': pengajuan_tempo.id, + 'user_company_id': partner_id.id, + 'tempo_duration': pengajuan_tempo.tempo_duration.id, + 'tempo_limit': pengajuan_tempo.tempo_limit, + }) - form_dokumen_data = kw.get('formDocs', False) - if form_dokumen_data: - try: - form_dokumen = json.loads(form_dokumen_data) + form_dokumen_data = kw.get('formDocs', False) + if form_dokumen_data: + try: + form_dokumen = json.loads(form_dokumen_data) - for dokumen in form_dokumen: - if dokumen['details']['base64'] != '': - mimetype, _ = mimetypes.guess_type(dokumen['details']['name']) - mimetype = mimetype or 'application/octet-stream' - data = base64.b64decode(dokumen['details']['base64']) - sppkp_attachment = request.env['ir.attachment'].create({ - 'name': dokumen['details']['name'], - 'type': 'binary', - 'datas': base64.b64encode(data), - 'res_model': 'user.pengajuan.tempo', - 'res_id': pengajuan_tempo.id, - 'mimetype': mimetype - }) + for dokumen in form_dokumen: + if dokumen['details']['base64'] != '': + mimetype, _ = mimetypes.guess_type(dokumen['details']['name']) + mimetype = mimetype or 'application/octet-stream' + data = base64.b64decode(dokumen['details']['base64']) + sppkp_attachment = request.env['ir.attachment'].create({ + 'name': dokumen['details']['name'], + 'type': 'binary', + 'datas': base64.b64encode(data), + 'res_model': 'user.pengajuan.tempo', + 'res_id': pengajuan_tempo.id, + 'mimetype': mimetype + }) - if dokumen['documentName'] == 'dokumenNib': - pengajuan_tempo.dokumen_nib = [(6, 0, [sppkp_attachment.id])] + if dokumen['documentName'] == 'dokumenNib': + pengajuan_tempo.dokumen_nib = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenSiup': - pengajuan_tempo.dokumen_siup = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenSiup': + pengajuan_tempo.dokumen_siup = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenTdp': - pengajuan_tempo.dokumen_tdp = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenTdp': + pengajuan_tempo.dokumen_tdp = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenSkdp': - pengajuan_tempo.dokumen_skdp = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenSkdp': + pengajuan_tempo.dokumen_skdp = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenSkt': - pengajuan_tempo.dokumen_skt = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenSkt': + pengajuan_tempo.dokumen_skt = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenNpwp': - pengajuan_tempo.dokumen_npwp = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenNpwp': + pengajuan_tempo.dokumen_npwp = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenSppkp': - pengajuan_tempo.dokumen_sppkp = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenSppkp': + pengajuan_tempo.dokumen_sppkp = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenAktaPerubahan': - pengajuan_tempo.dokumen_akta_perubahan = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenAktaPerubahan': + pengajuan_tempo.dokumen_akta_perubahan = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenKtpDirut': - pengajuan_tempo.dokumen_ktp_dirut = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenKtpDirut': + pengajuan_tempo.dokumen_ktp_dirut = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenAktaPendirian': - pengajuan_tempo.dokumen_akta_pendirian = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenAktaPendirian': + pengajuan_tempo.dokumen_akta_pendirian = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenLaporanKeuangan': - pengajuan_tempo.dokumen_laporan_keuangan = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenLaporanKeuangan': + pengajuan_tempo.dokumen_laporan_keuangan = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenFotoKantor': - pengajuan_tempo.dokumen_foto_kantor = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenFotoKantor': + pengajuan_tempo.dokumen_foto_kantor = [(6, 0, [sppkp_attachment.id])] - elif dokumen['documentName'] == 'dokumenTempatBekerja': - pengajuan_tempo.dokumen_tempat_bekerja = [(6, 0, [sppkp_attachment.id])] + elif dokumen['documentName'] == 'dokumenTempatBekerja': + pengajuan_tempo.dokumen_tempat_bekerja = [(6, 0, [sppkp_attachment.id])] - formatted_text = ''.join([' ' + char if char.isupper() and i != 0 else char for i, char in - enumerate(dokumen['documentName'])]) - teks = formatted_text.strip().title() - pengajuan_tempo.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) - if tempo_request: - user_tempo_request.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) + formatted_text = ''.join([' ' + char if char.isupper() and i != 0 else char for i, char in + enumerate(dokumen['documentName'])]) + teks = formatted_text.strip().title() + pengajuan_tempo.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) + if tempo_request: + user_tempo_request.message_post(body=teks, attachment_ids=[sppkp_attachment.id]) - except json.JSONDecodeError: - return http.Response(status=400, json_body={'error': 'Invalid JSON format for formDokumen'}) + except json.JSONDecodeError: + return http.Response(status=400, json_body={'error': 'Invalid JSON format for formDokumen'}) + + if tempo_request: + # pengajuan_tempo.user_id = id + template = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_request_tempo_review') + template.send_mail(pengajuan_tempo.id, force_send=True) + template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales') + template2.send_mail(pengajuan_tempo.id, force_send=True) + return self.response({ + 'id': pengajuan_tempo.id, + 'user_id': user_id, + }) + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') - if tempo_request: - # pengajuan_tempo.user_id = id - template = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_request_tempo_review') - template.send_mail(pengajuan_tempo.id, force_send=True) - template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales') - template2.send_mail(pengajuan_tempo.id, force_send=True) - return self.response({ - 'id': pengajuan_tempo.id, - 'user_id': user_id, - }) def get_user_by_email(self, email): return request.env['res.users'].search([ -- cgit v1.2.3 From 2ec2cdc415e01cebb11f838f29b1099f5fb1b818 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 6 Mar 2025 15:25:53 +0700 Subject: rounding other taxes --- indoteknik_custom/models/account_move.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py index 9aa0743b..45fdb8df 100644 --- a/indoteknik_custom/models/account_move.py +++ b/indoteknik_custom/models/account_move.py @@ -76,7 +76,8 @@ class AccountMove(models.Model): def compute_other_taxes(self): for rec in self: - rec.other_taxes = rec.other_subtotal * 0.12 + rec.other_taxes = round(rec.other_subtotal * 0.12, 2) + def compute_other_subtotal(self): for rec in self: -- cgit v1.2.3 From f8d121e96cf92222b61d41b9299e78c18165eef3 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 6 Mar 2025 16:12:27 +0700 Subject: fix code --- indoteknik_api/controllers/api_v1/partner.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 74e2b0aa..5a245a49 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -312,8 +312,9 @@ class Partner(controller.Controller): company_name = kw.get('name', pengajuan_tempo.name_tempo.name) partner_id = request.env['res.partner'].search([('name', 'like', company_name)], limit=1) user_account = self.get_user_by_email(user.email) - dokumen_prosedur = kw.get('formDokumenProsedur') if kw.get('formDokumenProsedur') != 'false' else False - + dokumen_prosedur = False + if kw.get('formDokumenProsedur') and kw.get('formDokumenProsedur') != 'false': + dokumen_prosedur = kw.get('formDokumenProsedur') params = self.get_request_params(kw, { # informasi perusahaan @@ -406,7 +407,10 @@ class Partner(controller.Controller): return self.response(code=500, description=f'Error creating partner data: {str(e)}') if partner_id: - pengajuan_tempo.name_tempo = partner_id + try: + pengajuan_tempo.name_tempo = partner_id + except Exception as e: + return self.response(code=500, description=f'Error updating partner data: {str(e)}') # Prosedur Pengiriman if dokumen_prosedur: @@ -458,8 +462,12 @@ class Partner(controller.Controller): category_produk_ids = kw.get('categoryProduk', False) category_ids = '' if category_produk_ids: - category_ids = list(map(int, category_produk_ids.split(','))) - pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] + try: + category_ids = list(map(int, category_produk_ids.split(','))) + pengajuan_tempo.category_produk_ids = [(6, 0, category_ids)] + except Exception as e: + return self.response(code=500, description=f'Unexpected error: {str(e)}') + tukar_invoice_input = kw.get('tukarInvoiceInput') if tukar_invoice_input: @@ -589,6 +597,9 @@ class Partner(controller.Controller): template.send_mail(pengajuan_tempo.id, force_send=True) template2 = pengajuan_tempo.env.ref('indoteknik_custom.mail_template_res_user_company_new_tempo_to_sales') template2.send_mail(pengajuan_tempo.id, force_send=True) + if not pengajuan_tempo: + return self.response(code=500, description="Failed to create or update pengajuan_tempo") + return self.response({ 'id': pengajuan_tempo.id, 'user_id': user_id, -- cgit v1.2.3 From ebfd584b6a1d44f15ce49fc8a6f9496818d7f377 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 6 Mar 2025 21:35:17 +0700 Subject: filter po indent --- indoteknik_custom/models/vendor_sla.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/indoteknik_custom/models/vendor_sla.py b/indoteknik_custom/models/vendor_sla.py index 852baa7a..b052e6cb 100644 --- a/indoteknik_custom/models/vendor_sla.py +++ b/indoteknik_custom/models/vendor_sla.py @@ -49,6 +49,10 @@ class VendorSLA(models.Model): date_of_transfer = fields.Datetime.to_datetime(po.date_done) po_confirmation_date = fields.Datetime.to_datetime(po.purchase_id.date_approve) if date_of_transfer < po_confirmation_date: continue + + days_difference = (date_of_transfer - po_confirmation_date).days + if days_difference > 14: + continue duration = (date_of_transfer - po_confirmation_date).total_seconds() / 3600 # Convert to hours total_duration += duration count += 1 -- cgit v1.2.3 From 3b8b4ec20d523cedf00d0a343ffc244e0a43da58 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 7 Mar 2025 10:39:00 +0700 Subject: when change name(individu) then nama pajak change --- indoteknik_custom/models/res_partner.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 7e574a72..fa2f6a81 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -470,4 +470,9 @@ class ResPartner(models.Model): if not self.nitku.isdigit(): raise UserError("NITKU harus berupa angka.") if len(self.nitku) != 22: - raise UserError("NITKU harus memiliki tepat 22 angka.") \ No newline at end of file + raise UserError("NITKU harus memiliki tepat 22 angka.") + + @api.onchange('name') + def _onchange_name(self): + if self.company_type == 'person': + self.nama_wajib_pajak = self.name \ No newline at end of file -- cgit v1.2.3 From c80a48e26b972b203229e128209bb8da89d5da57 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 10 Mar 2025 04:36:11 +0700 Subject: bugs fix rts date --- indoteknik_custom/models/sale_order.py | 64 +++++++++++++++++++++++++++---- indoteknik_custom/models/stock_picking.py | 36 ++++++++--------- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 4f85027a..e8bba662 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -450,7 +450,7 @@ class SaleOrder(models.Model): @api.depends('date_order', 'estimated_arrival_days', 'state', 'estimated_arrival_days_start') def _compute_eta_date(self): for rec in self: - if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days: + if rec.date_order and rec.state not in ['cancel'] and rec.estimated_arrival_days and rec.estimated_arrival_days_start: rec.eta_date = rec.date_order + timedelta(days=rec.estimated_arrival_days) rec.eta_date_start = rec.date_order + timedelta(days=rec.estimated_arrival_days_start) else: @@ -477,26 +477,74 @@ class SaleOrder(models.Model): break return offset + + # def calculate_sla_by_vendor(self, products): + # slatime = 15 + # for line in products: + # product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) + # slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 + + # return { + # 'slatime' : slatime + # } + + def calculate_sla_by_vendor(self, products): + product_ids = products.mapped('product_id.id') # Kumpulkan semua ID produk + include_instant = True # Default True, tetapi bisa menjadi False + + # Cek apakah SEMUA produk memiliki qty_free_bandengan >= qty_needed + all_fast_products = all(product.product_id.qty_free_bandengan >= product.product_uom_qty for product in products) + if all_fast_products: + return {'slatime': 1, 'include_instant': include_instant} + + + # Cari semua vendor pemenang untuk produk yang diberikan + vendors = self.env['purchase.pricelist'].search([ + ('product_id', 'in', product_ids), + ('is_winner', '=', True) + ]) + + max_slatime = 1 + + for vendor in vendors: + vendor_sla = self.env['vendor.sla'].search([('id_vendor', '=', vendor.vendor_id.id)], limit=1) + slatime = 15 + if vendor_sla: + if vendor_sla.unit == 'hari': + vendor_duration = vendor_sla.duration * 24 * 60 + else : + vendor_duration = vendor_sla.duration * 60 + include_instant = True + + estimation_sla = (1 * 24 * 60) + vendor_duration + estimation_sla_days = estimation_sla / (24 * 60) + slatime = math.ceil(estimation_sla_days) + + max_slatime = max(max_slatime, slatime) + + return {'slatime': max_slatime, 'include_instant': include_instant} @api.depends("order_line.product_id") def _compute_etrts_date(self): #Function to calculate Estimated Ready To Ship Date for rec in self: max_slatime = 1 # Default SLA jika tidak ada - for line in rec.order_line: - product_sla = self.env['product.sla'].search([('product_variant_id', '=', line.product_id.id)], limit=1) - # if(product_sla == False): - # continue - slatime = int(product_sla.sla) if product_sla and product_sla.sla and product_sla.sla != 'Indent' and "hari" in product_sla.sla.lower() else 15 - max_slatime = max(max_slatime, slatime) + slatime = self.calculate_sla_by_vendor(rec.order_line) + max_slatime = max(max_slatime, slatime['slatime']) if rec.date_order: - eta_date = rec.date_order + timedelta(days=self.get_days_until_next_business_day(rec.date_order)) + timedelta(days=max_slatime) + sum_days = max_slatime + self.get_days_until_next_business_day(rec.date_order) - 1 + if not rec.estimated_arrival_days: + rec.estimated_arrival_days = sum_days + + eta_date = rec.date_order + timedelta(days=sum_days) rec.estimated_ready_ship_date = eta_date rec.commitment_date = eta_date # Jika expected_ready_to_ship kosong, set nilai default if not rec.expected_ready_to_ship: rec.expected_ready_to_ship = eta_date + + @api.onchange('expected_ready_to_ship') #Hangle Onchange form Expected Ready to Ship def _onchange_expected_ready_ship_date(self): for rec in self: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 217f234e..b8bdcd94 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -190,26 +190,26 @@ class StockPicking(models.Model): self.env.cr.execute(query) - @api.depends('estimated_ready_ship_date', 'state') - def _callculate_sequance(self): - for record in self: - try : - if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): - rts = record.estimated_ready_ship_date - waktu.now() - rts_days = rts.days - rts_hours = divmod(rts.seconds, 3600) + # @api.depends('estimated_ready_ship_date', 'state') + # def _callculate_sequance(self): + # for record in self: + # try : + # if record.estimated_ready_ship_date and record.state not in ('cancel', 'done'): + # rts = record.estimated_ready_ship_date - waktu.now() + # rts_days = rts.days + # rts_hours = divmod(rts.seconds, 3600) - estimated_by_erts = rts.total_seconds() / 3600 + # estimated_by_erts = rts.total_seconds() / 3600 - record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" - record.countdown_hours = estimated_by_erts - else: - record.countdown_hours = 999999999999 - record.countdown_ready_to_ship = False - except Exception as e : - _logger.error(f"Error calculating sequance {record.id}: {str(e)}") - print(str(e)) - return { 'error': str(e) } + # record.countdown_ready_to_ship = f"{rts_days} days, {rts_hours} hours" + # record.countdown_hours = estimated_by_erts + # else: + # record.countdown_hours = 999999999999 + # record.countdown_ready_to_ship = False + # except Exception as e : + # _logger.error(f"Error calculating sequance {record.id}: {str(e)}") + # print(str(e)) + # return { 'error': str(e) } # @api.depends('estimated_ready_ship_date', 'state') -- cgit v1.2.3 From efdf162216dceff9b456b967d6ccd38da56fd1d5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 09:14:28 +0700 Subject: add reason cancel --- indoteknik_custom/models/sale_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e8bba662..aee19ec7 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -214,6 +214,7 @@ class SaleOrder(models.Model): ('dokumen_tidak_support', 'Indoteknik tidak bisa support document yang dibutuhkan (Ex: TKDN, COO, SNI)'), ('ganti_quotation', 'Ganti Quotation'), ('testing_internal', 'Testing Internal'), + ('revisi_data', 'Revisi Data'), ], string='Reason for Cancel', copy=False, index=True, tracking=3) attachment_bukti = fields.Many2one( 'ir.attachment', -- cgit v1.2.3 From 92b1ec69af1fb5b062307c7c7a00c3e0fb0e9e01 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 09:50:55 +0700 Subject: fix pengajuan tempo --- indoteknik_api/controllers/api_v1/partner.py | 2 +- indoteknik_custom/models/res_partner.py | 3 +++ indoteknik_custom/views/res_partner.xml | 1 + indoteknik_custom/views/user_pengajuan_tempo_request.xml | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 5a245a49..69ec8e69 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -426,7 +426,7 @@ class Partner(controller.Controller): 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) - pengajuan_tempo.message_post(body="SPPKP Uploaded", attachment_ids=[sppkp_attachment.id]) + pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[sppkp_attachment.id]) pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index 56c69f95..b5ed9ce2 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -75,6 +75,7 @@ class ResPartner(models.Model): zip_invoice = fields.Char(string="Zip") tukar_invoice = fields.Char(string='Jadwal Penukaran Invoice') jadwal_bayar = fields.Char(string='Jadwal Pembayaran') + dokumen_prosedur = fields.Many2one('ir.attachment', string="Dokumen Pengiriman", tracking=3, readonly=True) dokumen_pengiriman = fields.Char(string='Dokumen Tanda Terima yang Diberikan Pada Saat Pengiriman Barang') dokumen_pengiriman_input = fields.Char(string='Dokumen yang Dibawa Saat Pengiriman Barang') dokumen_invoice = fields.Char(string='Dokumen yang dilampirkan saat Pengiriman Invoice') @@ -273,6 +274,7 @@ class ResPartner(models.Model): vals['zip_invoice'] = vals.get('zip_invoice', self.zip_invoice) vals['tukar_invoice'] = vals.get('tukar_invoice', self.tukar_invoice) vals['jadwal_bayar'] = vals.get('jadwal_bayar', self.jadwal_bayar) + vals['dokumen_prosedur'] = vals.get('dokumen_prosedur', self.dokumen_prosedur) vals['dokumen_pengiriman'] = vals.get('dokumen_pengiriman', self.dokumen_pengiriman) vals['dokumen_pengiriman_input'] = vals.get('dokumen_pengiriman_input', self.dokumen_pengiriman_input) vals['dokumen_invoice'] = vals.get('dokumen_invoice', self.dokumen_invoice) @@ -344,6 +346,7 @@ class ResPartner(models.Model): 'zip_invoice': vals.get('zip_invoice'), 'tukar_invoice': vals.get('tukar_invoice'), 'jadwal_bayar': vals.get('jadwal_bayar'), + 'dokumen_prosedur': vals.get('dokumen_prosedur'), 'dokumen_pengiriman': vals.get('dokumen_pengiriman'), 'dokumen_pengiriman_input': vals.get('dokumen_pengiriman_input'), 'dokumen_invoice': vals.get('dokumen_invoice'), diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml index b87e616d..cb9fa3ac 100644 --- a/indoteknik_custom/views/res_partner.xml +++ b/indoteknik_custom/views/res_partner.xml @@ -152,6 +152,7 @@ + diff --git a/indoteknik_custom/views/user_pengajuan_tempo_request.xml b/indoteknik_custom/views/user_pengajuan_tempo_request.xml index 3ab00ed9..339ce8db 100644 --- a/indoteknik_custom/views/user_pengajuan_tempo_request.xml +++ b/indoteknik_custom/views/user_pengajuan_tempo_request.xml @@ -123,6 +123,7 @@ + -- cgit v1.2.3 From 8ded428f62eca10e114a72c50074b4d8afa36dc8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 10:09:24 +0700 Subject: fix tempo bug --- indoteknik_api/controllers/api_v1/partner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index 3e14a1dd..ebd91210 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -424,7 +424,7 @@ class Partner(controller.Controller): mimetype, _ = mimetypes.guess_type(dokumen_prosedur['name']) mimetype = mimetype or 'application/octet-stream' data = base64.b64decode(dokumen_prosedur['base64']) - sppkp_attachment = request.env['ir.attachment'].create({ + dokumen_prosedur_attachment = request.env['ir.attachment'].create({ 'name': dokumen_prosedur['name'], 'type': 'binary', 'datas': base64.b64encode(data), @@ -432,8 +432,8 @@ class Partner(controller.Controller): 'res_id': pengajuan_tempo.id, 'mimetype': mimetype }) - pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[sppkp_attachment.id]) - pengajuan_tempo.dokumen_prosedur = [(6, 0, [sppkp_attachment.id])] + pengajuan_tempo.dokumen_prosedur = [(6, 0, [dokumen_prosedur_attachment.id])] + pengajuan_tempo.message_post(body="Dokumen Prosedur", attachment_ids=[dokumen_prosedur_attachment.id]) form_supplier_data = kw.get('formSupplier', False) -- cgit v1.2.3 From 447edfb87520d5d1475eeba43f1cd3425222cd4d Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 10 Mar 2025 10:19:18 +0700 Subject: udate final_seq --- indoteknik_custom/models/sale_order.py | 1 + indoteknik_custom/models/stock_picking.py | 1 + 2 files changed, 2 insertions(+) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index e8bba662..498f2467 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -512,6 +512,7 @@ class SaleOrder(models.Model): if vendor_sla: if vendor_sla.unit == 'hari': vendor_duration = vendor_sla.duration * 24 * 60 + include_instant = False else : vendor_duration = vendor_sla.duration * 60 include_instant = True diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index b8bdcd94..ab8109c7 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -934,6 +934,7 @@ class StockPicking(models.Model): self.calculate_line_no() self.date_done = datetime.datetime.utcnow() self.state_reserve = 'done' + self.final_seq = 0 self.send_mail_bills() return res -- cgit v1.2.3 From 685c07a85cdad186ec14ad7bf9b78d52b34f8cb5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 20:57:43 +0700 Subject: update code ke right branch --- indoteknik_custom/models/stock_inventory.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index 12a891de..b7020285 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -29,10 +29,10 @@ class StockInventory(models.Model): """Menentukan nomor berdasarkan kategori Adjust-In atau Adjust-Out.""" name_upper = record.name.upper() if record.name else "" - if self.adjusment_type == 'out' or "ADJUST OUT" in name_upper or "ADJUST-OUT" in name_upper or "OUT" in name_upper: + if self.adjusment_type == 'out': last_number = self._get_last_sequence("ADJUST/OUT/") record.number = f"ADJUST/OUT/{last_number}" - elif self.adjusment_type == 'in' or "ADJUST IN" in name_upper or "ADJUST-IN" in name_upper or "IN" in name_upper: + elif self.adjusment_type == 'in': last_number = self._get_last_sequence("ADJUST/IN/") record.number = f"ADJUST/IN/{last_number}" else: -- cgit v1.2.3 From ba57391edba128d8f58243d0dfd0d16a38e7dfec Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 10 Mar 2025 21:54:48 +0700 Subject: when duplikat adjust doc number dont create first --- indoteknik_custom/models/stock_inventory.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py index b7020285..69cca5bc 100644 --- a/indoteknik_custom/models/stock_inventory.py +++ b/indoteknik_custom/models/stock_inventory.py @@ -54,6 +54,26 @@ class StockInventory(models.Model): @api.model def create(self, vals): + """Pastikan nomor hanya dibuat saat penyimpanan.""" + if 'adjusment_type' in vals and not vals.get('number'): + vals['number'] = False # Jangan buat number otomatis dulu + order = super(StockInventory, self).create(vals) - self._assign_number(order) + + if order.adjusment_type: + self._assign_number(order) # Generate number setelah save + return order + + def write(self, vals): + """Jika adjusment_type diubah, generate ulang nomor.""" + res = super(StockInventory, self).write(vals) + if 'adjusment_type' in vals: + for record in self: + self._assign_number(record) + return res + + def copy(self, default=None): + """Saat duplikasi, adjusment_type dikosongkan dan number tidak ikut terduplikasi.""" + default = dict(default or {}, adjusment_type=False, number=False) + return super(StockInventory, self).copy(default=default) -- cgit v1.2.3 From 56e42190cb5cf039ee3ad05068dc7fb94f72943d Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 11 Mar 2025 09:26:23 +0700 Subject: cr alert duplicate name --- indoteknik_custom/models/res_partner.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/indoteknik_custom/models/res_partner.py b/indoteknik_custom/models/res_partner.py index fe04535c..fd3a0514 100644 --- a/indoteknik_custom/models/res_partner.py +++ b/indoteknik_custom/models/res_partner.py @@ -127,8 +127,8 @@ class ResPartner(models.Model): ('PNR', 'Pareto Non Repeating'), ('NP', 'Non Pareto') ]) - email_finance = fields.Char(string='Email Finance') - email_sales = fields.Char(string='Email Sales') + email_finance = fields.Char(string='Email Finance Vendor') + email_sales = fields.Char(string='Email Sales Vendor') user_payment_terms_sales = fields.Many2one('res.users', string='Users Update Payment Terms') date_payment_terms_sales = fields.Datetime(string='Date Update Payment Terms') @@ -191,10 +191,10 @@ class ResPartner(models.Model): def _check_duplicate_name(self): for record in self: if record.name: - # Mencari partner lain yang memiliki nama sama (case-insensitive) existing_partner = self.env['res.partner'].search([ - ('id', '!=', record.id), # Hindari mencocokkan diri sendiri - ('name', '=', record.name) # Case-insensitive search + ('id', '!=', record.id), + ('name', '=', record.name), + ('email', '=', record.email) ], limit=1) if existing_partner: -- cgit v1.2.3 From 351580ed5c3266a7fc956eb77efa1d2d2d0c5d57 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Wed, 12 Mar 2025 13:28:29 +0700 Subject: cr estimate shipping price --- indoteknik_custom/models/sale_order.py | 59 +++++++++++++++++++------- indoteknik_custom/security/ir.model.access.csv | 1 + indoteknik_custom/views/sale_order.xml | 1 + 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 852e3cf0..14a8e688 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -51,6 +51,16 @@ class CancelReasonOrder(models.TransientModel): order.confirm_cancel_order() return {'type': 'ir.actions.act_window_close'} + +class ShippingOption(models.Model): + _name = "shipping.option" + _description = "Shipping Option" + + name = fields.Char(string="Option Name", required=True) + price = fields.Float(string="Price", required=True) + provider = fields.Char(string="Provider") + etd = fields.Char(string="Estimated Delivery Time") + sale_order_id = fields.Many2one('sale.order', string="Sale Order", ondelete="cascade") class SaleOrder(models.Model): _inherit = "sale.order" @@ -221,6 +231,12 @@ class SaleOrder(models.Model): string="Attachment Bukti Cancel", readonly=False, ) nomor_so_pengganti = fields.Char(string='Nomor SO Pengganti', copy=False, tracking=3) + shipping_option_id = fields.Many2one("shipping.option", string="Selected Shipping Option", domain="['|', ('sale_order_id', '=', False), ('sale_order_id', '=', id)]") + + @api.constrains('shipping_option_id') + def _check_shipping_option(self): + for rec in self: + rec.delivery_amt = rec.shipping_option_id.price def _compute_shipping_method_picking(self): for order in self: @@ -268,15 +284,26 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - + if total_weight < 10: total_weight = 10 + self.delivery_amt = total_weight * 3000 + + shipping_option = self.env["shipping.option"].create({ + "name": "Indoteknik Delivery", + "price": self.delivery_amt, + "provider": "Indoteknik", + "etd": "1-2 Hari", + "sale_order_id": self.id, + }) + self.shipping_option_id = shipping_option.id def action_estimate_shipping(self): if self.carrier_id.id in [1, 151]: self.action_indoteknik_estimate_shipping() return + total_weight = 0 missing_weight_products = [] @@ -294,33 +321,35 @@ class SaleOrder(models.Model): if total_weight == 0: raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") - # Mendapatkan city_id berdasarkan nama kota - origin_city_name = self.warehouse_id.partner_id.kota_id.name destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id - if not destination_subsdistrict_id: - raise UserError("Gagal mendapatkan ID kota asal atau tujuan.") + raise UserError("Gagal mendapatkan ID kota tujuan.") result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id) if result: - estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value'] - self.delivery_amt = estimated_cost - - shipping_info = [] + shipping_options = [] for courier in result['rajaongkir']['results']: for cost_detail in courier['costs']: service = cost_detail['service'] description = cost_detail['description'] etd = cost_detail['cost'][0]['etd'] value = cost_detail['cost'][0]['value'] - shipping_info.append(f"Service: {service}, Description: {description}, ETD: {etd} hari, Cost: Rp {value}") + shipping_options.append((service, description, etd, value, courier['code'])) + + self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink() + + for service, description, etd, value, provider in shipping_options: + self.env["shipping.option"].create({ + "name": service, + "price": value, + "provider": provider, + "etd": etd, + "sale_order_id": self.id, + }) - log_message = "
".join(shipping_info) + self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id - description_ongkir = result['rajaongkir']['results'][0]['costs'][0]['description'] - etd_ongkir = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['etd'] - service_ongkir = result['rajaongkir']['results'][0]['costs'][0]['service'] - self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
Service: {service_ongkir}
Description: {description_ongkir}
ETD: {etd_ongkir}
Detail Lain:
{log_message}") + self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}
Detail Lain:
{'
'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}") else: raise UserError("Gagal mendapatkan estimasi ongkir.") diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv index 4d9d8cf7..4d0e51eb 100755 --- a/indoteknik_custom/security/ir.model.access.csv +++ b/indoteknik_custom/security/ir.model.access.csv @@ -167,3 +167,4 @@ access_barcoding_product_line,access.barcoding.product.line,model_barcoding_prod access_account_payment_register,access.account.payment.register,model_account_payment_register,,1,1,1,1 access_stock_inventory,access.stock.inventory,model_stock_inventory,,1,1,1,1 access_cancel_reason_order,cancel.reason.order,model_cancel_reason_order,,1,1,1,0 +access_shipping_option,shipping.option,model_shipping_option,,1,1,1,1 diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index ebee64b1..0d190f37 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -100,6 +100,7 @@ + -- cgit v1.2.3 From 731a47bee9925d65afe45f870880e9a42104fe7f Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 13 Mar 2025 08:54:55 +0700 Subject: add grouped so number and invoice number on customer commision --- indoteknik_custom/models/commision.py | 15 +++++++++++++++ indoteknik_custom/views/customer_commision.xml | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py index 6920154a..0d31e954 100644 --- a/indoteknik_custom/models/commision.py +++ b/indoteknik_custom/models/commision.py @@ -157,7 +157,22 @@ class CustomerCommision(models.Model): ('pending', 'Pending'), ('payment', 'Payment'), ], string='Payment Status', copy=False, readonly=True, tracking=3, default='pending') + grouped_so_number = fields.Char(string='Group SO Number', compute='_compute_grouped_numbers') + grouped_invoice_number = fields.Char(string='Group Invoice Number', compute='_compute_grouped_numbers') + def _compute_grouped_numbers(self): + for rec in self: + so_numbers = set() + invoice_numbers = set() + + for line in rec.commision_lines: + if line.invoice_id: + if line.invoice_id.sale_id: + so_numbers.add(line.invoice_id.sale_id.name) + invoice_numbers.add(line.invoice_id.name) + + rec.grouped_so_number = ', '.join(sorted(so_numbers)) + rec.grouped_invoice_number = ', '.join(sorted(invoice_numbers)) # add status for type of commision, fee, rebate / cashback # include child or not? diff --git a/indoteknik_custom/views/customer_commision.xml b/indoteknik_custom/views/customer_commision.xml index 51172b1c..bb1628bc 100644 --- a/indoteknik_custom/views/customer_commision.xml +++ b/indoteknik_custom/views/customer_commision.xml @@ -17,6 +17,8 @@ decoration-danger="payment_status == 'pending'" widget="badge"/> + +
@@ -62,6 +64,8 @@ + +
-- cgit v1.2.3 From 1ff9a57e2f7a7ecb3ba9321f7133f43e7009aa47 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 13 Mar 2025 09:47:27 +0700 Subject: back to longtitude --- indoteknik_api/controllers/api_v1/partner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py index ebd91210..126fded4 100644 --- a/indoteknik_api/controllers/api_v1/partner.py +++ b/indoteknik_api/controllers/api_v1/partner.py @@ -78,7 +78,7 @@ class Partner(controller.Controller): 'district_id': ['number', 'alias:kecamatan_id'], 'sub_district_id': ['number', 'alias:kelurahan_id', 'exclude_if_null'], 'zip': ['required'], - 'longitude': '', # Perbaikan dari longtitude ke longitude + 'longtitude': '', 'latitude': '', 'address_map': [], 'alamat_lengkap_text': [] -- cgit v1.2.3