diff options
| author | IT Fixcomart <it@fixcomart.co.id> | 2023-09-01 06:33:29 +0000 |
|---|---|---|
| committer | IT Fixcomart <it@fixcomart.co.id> | 2023-09-01 06:33:29 +0000 |
| commit | 532336ef618e2e4b57092180e8d3ecf3c3b82bf2 (patch) | |
| tree | 0376b42769d70df260d0f411a98514a8b1d3f995 | |
| parent | 41e76bb5b668473c8086ba82ea8f405315b8e5ae (diff) | |
| parent | 3757621d0b0232603a3d578b27b845054cae5982 (diff) | |
Merged in change/feature/stock-picking (pull request #122)
Change/feature/stock picking
| -rw-r--r-- | indoteknik_api/controllers/api_v1/__init__.py | 3 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/sale_order.py | 12 | ||||
| -rw-r--r-- | indoteknik_api/controllers/api_v1/stock_picking.py | 81 | ||||
| -rw-r--r-- | indoteknik_api/models/sale_order.py | 8 | ||||
| -rwxr-xr-x | indoteknik_custom/models/sale_order.py | 1 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 101 | ||||
| -rwxr-xr-x | indoteknik_custom/views/sale_order.xml | 1 |
7 files changed, 199 insertions, 8 deletions
diff --git a/indoteknik_api/controllers/api_v1/__init__.py b/indoteknik_api/controllers/api_v1/__init__.py index 65bcf926..36fcbd53 100644 --- a/indoteknik_api/controllers/api_v1/__init__.py +++ b/indoteknik_api/controllers/api_v1/__init__.py @@ -25,4 +25,5 @@ from . import content from . import midtrans from . import wati from . import courier -from . import voucher
\ No newline at end of file +from . import voucher +from . import stock_picking
\ No newline at end of file diff --git a/indoteknik_api/controllers/api_v1/sale_order.py b/indoteknik_api/controllers/api_v1/sale_order.py index ecc6c771..4f6393a6 100644 --- a/indoteknik_api/controllers/api_v1/sale_order.py +++ b/indoteknik_api/controllers/api_v1/sale_order.py @@ -97,12 +97,8 @@ class SaleOrder(controller.Controller): if not params['valid']: return self.response(code=400, description=params) - partner_child_ids = self.get_partner_child_ids( - params['value']['partner_id']) - domain = [ - ('id', '=', params['value']['id']), - ('partner_id', 'in', partner_child_ids) - ] + partner_child_ids = self.get_partner_child_ids(params['value']['partner_id']) + domain = [('id', '=', params['value']['id']), ('partner_id', 'in', partner_child_ids)] data = {} sale_order = request.env['sale.order'].search(domain) if sale_order: @@ -262,7 +258,8 @@ class SaleOrder(controller.Controller): 'carrier_id': [], 'delivery_service_type': [], 'voucher': [], - 'source': [] + 'source': [], + 'estimated_arrival_days': ['number', 'default:0'] }) if not params['valid']: @@ -287,6 +284,7 @@ class SaleOrder(controller.Controller): 'partner_purchase_order_name': params['value']['po_number'], 'partner_purchase_order_file': params['value']['po_file'], 'delivery_amt': params['value']['delivery_amount'], + 'estimated_arrival_days': params['value']['estimated_arrival_days'], '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 new file mode 100644 index 00000000..e0a60c98 --- /dev/null +++ b/indoteknik_api/controllers/api_v1/stock_picking.py @@ -0,0 +1,81 @@ +from .. import controller +from odoo import http +from odoo.http import request + + +class StockPicking(controller.Controller): + prefix = '/api/v1/' + PREFIX_PARTNER = prefix + 'partner/<partner_id>/' + + @http.route(PREFIX_PARTNER + 'stock-picking', auth='public', method=['GET', 'OPTIONS']) + @controller.Controller.must_authorized(private=True, private_key='partner_id') + def get_partner_stock_picking(self, **kw): + get_params = self.get_request_params(kw, { + 'partner_id': ['number'], + 'q': [], + 'limit': ['default:0', 'number'], + 'offset': ['default:0', 'number'] + }) + + if not get_params['valid']: + return self.response(code=400, description=get_params) + + params = get_params['value'] + partner_id = params['partner_id'] + limit = params['limit'] + offset = params['offset'] + + child_ids = request.env['res.partner'].browse(partner_id).get_child_ids() + + picking_model = request.env['stock.picking'] + default_domain = [('partner_id', 'in', child_ids), ('sale_id', '!=', False), ('origin', 'ilike', 'SO%'), ('state', '!=', 'cancel')] + + domain = default_domain + if params['q']: + query_like = '%' + params['q'].replace(' ', '%') + '%' + domain += ['|', '|', ('name', 'ilike', query_like), ('sale_id.client_order_ref', 'ilike', query_like), ('delivery_tracking_no', 'ilike', query_like)] + + stock_pickings = picking_model.search(domain, offset=offset, limit=limit, order='create_date desc') + res_pickings = [] + for picking in stock_pickings: + manifests = picking.get_manifests() + res_pickings.append({ + 'id': picking.id, + 'name': picking.name, + 'date': self.time_to_str(picking.create_date, '%d/%m/%Y'), + 'tracking_number': picking.delivery_tracking_no or '', + 'sale_order': { + 'id': picking.sale_id.id, + 'name': picking.sale_id.name, + 'client_order_ref': picking.sale_id.client_order_ref or '' + }, + 'delivered': picking.waybill_id.delivered or picking.driver_arrival_date != False, + 'carrier_name': picking.carrier_id.name or '', + 'last_manifest': next(iter(manifests), None) + }) + + pending_count = picking_model.search_count(default_domain + [('driver_departure_date', '=', False), ('driver_arrival_date', '=', False)]) + shipment_count = picking_model.search_count(default_domain + [('driver_departure_date', '!=', False), ('driver_arrival_date', '=', False)]) + completed_count = picking_model.search_count(default_domain + [('driver_departure_date', '!=', False), ('driver_arrival_date', '!=', False)]) + + return self.response({ + 'summary': { + 'pending_count': pending_count, + 'shipment_count': shipment_count, + 'completed_count': completed_count + }, + 'picking_total': picking_model.search_count(default_domain), + 'pickings': res_pickings + }) + + @http.route(PREFIX_PARTNER + 'stock-picking/<id>/tracking', auth='public', method=['GET', 'OPTIONS']) + @controller.Controller.must_authorized(private=True, private_key='partner_id') + def get_partner_stock_picking_detail_tracking(self, **kw): + id = int(kw.get('id', 0)) + picking_model = request.env['stock.picking'] + + picking = picking_model.browse(id) + if not picking: + return self.response(None) + + return self.response(picking.get_tracking_detail()) diff --git a/indoteknik_api/models/sale_order.py b/indoteknik_api/models/sale_order.py index 9661a0ba..85bf5015 100644 --- a/indoteknik_api/models/sale_order.py +++ b/indoteknik_api/models/sale_order.py @@ -18,7 +18,15 @@ class SaleOrder(models.Model): 'invoice_count': sale_order.invoice_count, 'status': 'draft', 'date_order': self.env['rest.api'].datetime_to_str(sale_order.date_order, '%d/%m/%Y %H:%M:%S'), + 'pickings': [] } + for picking in sale_order.picking_ids: + data['pickings'].append({ + 'id': picking.id, + 'name': picking.name, + 'tracking_number': picking.delivery_tracking_no or '', + 'delivered': picking.waybill_id.delivered or picking.driver_arrival_date != False, + }) if sale_order.state == 'cancel': data['status'] = 'cancel' if sale_order.state in ['draft', 'sent']: diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index a0bc54b8..65132fda 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -76,6 +76,7 @@ class SaleOrder(models.Model): voucher_id = fields.Many2one(comodel_name='voucher', string='Voucher') amount_voucher_disc = fields.Float(string='Voucher Discount') source_id = fields.Many2one('utm.source', 'Source', domain="[('id', 'in', [32, 59, 60, 61])]") + estimated_arrival_days = fields.Integer('Estimated Arrival Days', default=0) def _compute_purchase_total(self): for order in self: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 1ffb9aef..8e8c1e79 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -2,6 +2,7 @@ from odoo import fields, models, api, _ from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tools.float_utils import float_is_zero from itertools import groupby +import pytz, datetime class StockPicking(models.Model): @@ -354,3 +355,103 @@ class StockPicking(models.Model): picking.delivery_status = "Diterima Ekspedisi" else: picking.delivery_status = "Diterima Konsumen" + + def create_manifest_data(self, description, object): + datetime_str = '' + if isinstance(object, datetime.datetime): + jakarta_timezone = pytz.timezone('Asia/Jakarta') + datetime_str = object.replace(tzinfo=pytz.utc).astimezone(jakarta_timezone).strftime('%Y-%m-%d %H:%M:%S') + return { + 'description': description, + 'datetime': datetime_str + } + + def get_manifests(self): + if self.waybill_id and len(self.waybill_id.manifest_ids) > 0: + return [self.create_manifest_data(x.description, x.datetime) for x in self.waybill_id.manifest_ids] + + status_mapping = { + 'pickup': { + 'arrival': 'Sudah diambil', + 'departure': 'Siap diambil', + 'prepare': 'Sedang disiapkan' + }, + 'delivery': { + 'arrival': 'Sudah sampai', + 'departure': 'Sedang dikirim', + 'prepare': 'Menunggu pickup', + } + } + + status_key = 'delivery' + if self.carrier_id.id == 32: + status_key = 'pickup' + + manifest_datas = [] + departure_date = self.driver_departure_date + arrival_date = self.driver_arrival_date + status = status_mapping.get(status_key) + + if not status: + return manifest_datas + + if arrival_date: + manifest_datas.append(self.create_manifest_data(status['arrival'], arrival_date)) + if departure_date: + manifest_datas.append(self.create_manifest_data(status['departure'], departure_date)) + manifest_datas.append(self.create_manifest_data(status['prepare'], self.create_date)) + + return manifest_datas + + def get_tracking_detail(self): + self.ensure_one() + + response = { + 'delivery_order': { + 'name': self.name, + 'carrier': self.carrier_id.name or '', + 'receiver_name': '', + 'receiver_city': '' + }, + 'delivered': False, + 'waybill_number': self.delivery_tracking_no or '', + 'delivery_status': None, + 'eta': self.generate_eta_delivery(), + 'manifests': self.get_manifests() + } + + if not self.waybill_id or len(self.waybill_id.manifest_ids) == 0: + response['delivered'] = self.driver_arrival_date != False + return response + + response['delivery_order']['receiver_name'] = self.waybill_id.receiver_name + response['delivery_order']['receiver_city'] = self.waybill_id.receiver_city + response['delivery_status'] = self.waybill_id._get_history('delivery_status') + response['delivered'] = self.waybill_id.delivered + + return response + + def generate_eta_delivery(self): + current_date = datetime.datetime.now() + prepare_days = 3 + start_date = self.driver_departure_date or self.create_date + + ead = self.sale_id.estimated_arrival_days or 0 + if not self.driver_departure_date: + ead += prepare_days + + ead_datetime = datetime.timedelta(days=ead) + fastest_eta = start_date + ead_datetime + if not self.driver_departure_date and fastest_eta < current_date: + fastest_eta = current_date + ead_datetime + + longest_days = 3 + longest_eta = fastest_eta + datetime.timedelta(days=longest_days) + + format_time = '%d %b %Y' + format_time_fastest = '%d %b' if fastest_eta.year == longest_eta.year else format_time + + formatted_fastest_eta = fastest_eta.strftime(format_time_fastest) + formatted_longest_eta = longest_eta.strftime(format_time) + + return f'{formatted_fastest_eta} - {formatted_longest_eta}' diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml index 14f3663f..91ce1463 100755 --- a/indoteknik_custom/views/sale_order.xml +++ b/indoteknik_custom/views/sale_order.xml @@ -93,6 +93,7 @@ </field> <field name="effective_date" position="after"> <field name="carrier_id"/> + <field name="estimated_arrival_days"/> </field> <page name="customer_signature" position="after"> |
