diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2024-12-09 15:05:00 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2024-12-09 15:05:00 +0700 |
| commit | 31374ed669121ba6c8ec401f82ad0bfedf07a6d6 (patch) | |
| tree | 0aa9bf91134ad7fe07142c37873ddeed3b6f3c71 | |
| parent | 02eacd54387953f42a884a22544e2f7c94081536 (diff) | |
| parent | 2123dbaab7b4ff49d90336d34e2be76e8eb07f8e (diff) | |
Merge branch 'production' into iman/pengajuan-tempo
| -rw-r--r-- | indoteknik_api/controllers/controller.py | 30 | ||||
| -rw-r--r-- | indoteknik_api/models/res_users.py | 2 | ||||
| -rwxr-xr-x | indoteknik_custom/models/product_template.py | 4 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking.py | 122 | ||||
| -rw-r--r-- | indoteknik_custom/models/stock_picking_return.py | 14 | ||||
| -rw-r--r-- | indoteknik_custom/views/stock_picking.xml | 23 |
6 files changed, 182 insertions, 13 deletions
diff --git a/indoteknik_api/controllers/controller.py b/indoteknik_api/controllers/controller.py index a34a2688..80f45074 100644 --- a/indoteknik_api/controllers/controller.py +++ b/indoteknik_api/controllers/controller.py @@ -4,6 +4,7 @@ import functools import io import json from array import array +from io import BytesIO import jwt from odoo import http @@ -11,6 +12,8 @@ from odoo.http import request from odoo.modules import get_module_resource from odoo.tools.config import config from PIL import Image +from PIL.WebPImagePlugin import Image +from PIL import features from pytz import timezone @@ -204,6 +207,8 @@ class Controller(http.Controller): if not variant: image = self.add_watermark_to_image(image, ratio, version) + # image = self.convert_to_webp(image) + response_headers = [ ('Content-Type', 'image/jpg'), ('Cache-Control', 'public, max-age=3600') @@ -214,6 +219,31 @@ class Controller(http.Controller): response_headers ) + def convert_to_webp(self, image_base64): + """Convert image from base64 to WebP format and return base64 WebP.""" + try: + print(f"Image base64 length: {len(image_base64)}") + + # Decode Base64 to Bytes + image_data = base64.b64decode(image_base64) + image = Image.open(BytesIO(image_data)) + + if image.format == "PNG" and image.mode != "RGBA": + image = image.convert("RGBA") + + # Convert to WebP + with BytesIO() as output: + image.save(output, format="WEBP", quality=85) + webp_data = output.getvalue() + + # Encode back to Base64 + return base64.b64encode(webp_data).decode('utf-8') + except Exception as e: + print(f"Error details: {e}") + # If conversion fails, return the original image + request.env.cr.rollback() # Rollback any transactions + return image_base64 + def add_watermark_to_image(self, image, ratio, version = '1'): if not image: return '' diff --git a/indoteknik_api/models/res_users.py b/indoteknik_api/models/res_users.py index 93204a96..d9329f67 100644 --- a/indoteknik_api/models/res_users.py +++ b/indoteknik_api/models/res_users.py @@ -79,7 +79,7 @@ class ResUsers(models.Model): data['state_id'] = { 'id': user.state_id.id, 'name': user.state_id.name - } or None + } or 0 if user.kota_id: data['city'] = { diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py index 4d186568..6fb8c7a0 100755 --- a/indoteknik_custom/models/product_template.py +++ b/indoteknik_custom/models/product_template.py @@ -66,7 +66,7 @@ class ProductTemplate(models.Model): @api.constrains('name', 'internal_reference', 'x_manufacture') def required_public_categ_ids(self): for rec in self: - if not rec.public_categ_ids: + if not rec.public_categ_ids and rec.type == 'product': raise UserError('Field Categories harus diisi') def _get_qty_sold(self): @@ -388,7 +388,7 @@ class ProductProduct(models.Model): @api.constrains('name', 'internal_reference', 'x_manufacture') def required_public_categ_ids(self): for rec in self: - if not rec.public_categ_ids: + if not rec.public_categ_ids and rec.type == 'product': raise UserError('Field Categories harus diisi') @api.constrains('active') diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 03b10cdb..e6506a0b 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -1,9 +1,11 @@ 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 datetime +from datetime import timedelta, datetime from itertools import groupby -import pytz, datetime, requests, json +import pytz, requests, json, requests +from dateutil import parser +import datetime class StockPicking(models.Model): @@ -113,6 +115,120 @@ class StockPicking(models.Model): ], string='Status Reserve', readonly=True, tracking=True, help="The current state of the stock picking.") notee = fields.Text(string="Note") + # Envio Tracking Section + envio_id = fields.Char(string="Envio ID", readonly=True) + envio_code = fields.Char(string="Envio Code", readonly=True) + envio_ref_code = fields.Char(string="Envio Reference Code", readonly=True) + envio_eta_at = fields.Datetime(string="Estimated Time of Arrival (ETA)", readonly=True) + envio_ata_at = fields.Datetime(string="Actual Time of Arrival (ATA)", readonly=True) + envio_etd_at = fields.Datetime(string="Estimated Time of Departure (ETD)", readonly=True) + envio_atd_at = fields.Datetime(string="Actual Time of Departure (ATD)", readonly=True) + envio_received_by = fields.Char(string="Received By", readonly=True) + envio_status = fields.Char(string="Status", readonly=True) + envio_cod_value = fields.Float(string="COD Value", readonly=True) + envio_cod_status = fields.Char(string="COD Status", readonly=True) + envio_logs = fields.Text(string="Logs", readonly=True) + envio_latest_message = fields.Text(string="Latest Log Message", readonly=True) + envio_latest_recorded_at = fields.Datetime(string="Log Recorded At", readonly=True) + envio_latest_latitude = fields.Float(string="Log Latitude", readonly=True) + envio_latest_longitude = fields.Float(string="Log Longitude", readonly=True) + tracking_by = fields.Many2one('res.users', string='Tracking By', readonly=True, tracking=True) + + def _convert_to_wib(self, date_str): + """ + Mengonversi string waktu ISO 8601 ke format waktu Indonesia (WIB) + """ + if not date_str: + return False + try: + utc_time = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + wib_time = utc_time + timedelta(hours=7) + return wib_time.strftime('%d-%m-%Y %H:%M:%S') + except ValueError: + raise UserError(f"Format waktu tidak sesuai: {date_str}") + + def _convert_to_datetime(self, date_str): + """Mengonversi string waktu dari API ke datetime.""" + if not date_str: + return False + try: + # Format waktu dengan milidetik + date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%S.%fZ') + return date + except ValueError: + try: + # Format waktu tanpa milidetik + date = datetime.datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ') + return date + except ValueError: + raise UserError(f"Format waktu tidak sesuai: {date_str}") + + def track_envio_shipment(self): + pickings = self.env['stock.picking'].search([ + ('picking_type_code', '=', 'outgoing'), + ('state', '=', 'done'), + ('carrier_id', '=', 151) + ]) + for picking in pickings: + if not picking.name: + raise UserError("Name pada stock.picking tidak ditemukan.") + + # API URL dan headers + url = f"https://api.envio.co.id/v1/tracking/distribution?code={picking.name}" + headers = { + 'Authorization': 'Bearer JZ0Seh6qpYJAC3CJHdhF7sPqv8B/uSSfZe1VX5BL?vPYdo', + 'Content-Type': 'application/json', + } + + try: + # Request ke API + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() # Raise error jika status code bukan 200 + response_data = response.json() + + # Validasi jika respons tidak sesuai format yang diharapkan + if not response_data or "data" not in response_data: + raise UserError("Respons API tidak sesuai format yang diharapkan.") + + data = response_data.get("data") + if not data: + continue + + # Menyimpan data ke field masing-masing + picking.envio_id = data.get("id") + picking.envio_code = data.get("code") + picking.envio_ref_code = data.get("ref_code") + picking.envio_eta_at = self._convert_to_datetime(data.get("eta_at")) + picking.envio_ata_at = self._convert_to_datetime(data.get("ata_at")) + picking.envio_etd_at = self._convert_to_datetime(data.get("etd_at")) + picking.envio_atd_at = self._convert_to_datetime(data.get("atd_at")) + picking.envio_received_by = data.get("received_by") + picking.envio_status = data.get("status") + picking.envio_cod_value = data.get("cod_value", 0.0) + picking.envio_cod_status = data.get("cod_status") + + # Menyimpan log terbaru + logs = data.get("logs", []) + if logs and isinstance(logs, list) and logs[0]: + latest_log = logs[0] + picking.envio_latest_message = latest_log.get("message", "Log kosong.") + picking.envio_latest_recorded_at = self._convert_to_datetime(latest_log.get("recorded_at")) + picking.envio_latest_latitude = latest_log.get("latitude", 0.0) + picking.envio_latest_longitude = latest_log.get("longitude", 0.0) + + 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 + picking.envio_ata_at = False + except requests.exceptions.RequestException as e: + raise UserError(f"Terjadi kesalahan saat menghubungi API Envio: {str(e)}") + except Exception as e: + 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" @@ -539,7 +655,7 @@ class StockPicking(models.Model): qty_onhand = check_qty_per_inventory(self, line.product_id, line.location_id) if line.qty_done > qty_onhand: - raise UserError(f'{line.product_id.display_name} : Quantity Done melebihi Quantity Onhand') + raise UserError('Quantity Done melebihi Quantity Onhand') def button_validate(self): if not self.env.user.is_logistic_approver and self.env.context.get('active_model') == 'stock.picking': diff --git a/indoteknik_custom/models/stock_picking_return.py b/indoteknik_custom/models/stock_picking_return.py index 91a3a9fd..d4347235 100644 --- a/indoteknik_custom/models/stock_picking_return.py +++ b/indoteknik_custom/models/stock_picking_return.py @@ -14,14 +14,14 @@ class ReturnPicking(models.TransientModel): ('id', '=', res['picking_id']), ]) - sale_id = stock_picking.group_id.sale_id - if not stock_picking.approval_return_status == 'approved' and sale_id.invoice_ids: + # sale_id = stock_picking.group_id.sale_id + if not stock_picking.approval_return_status == 'approved': raise UserError('Harus Approval Accounting AR untuk melakukan Retur') - purchase = self.env['purchase.order'].search([ - ('name', '=', stock_picking.group_id.name), - ]) - if not stock_picking.approval_return_status == 'approved' and purchase.invoice_ids: - raise UserError('Harus Approval Accounting AP untuk melakukan Retur') + # purchase = self.env['purchase.order'].search([ + # ('name', '=', stock_picking.group_id.name), + # ]) + # if not stock_picking.approval_return_status == 'approved' and purchase.invoice_ids: + # raise UserError('Harus Approval Accounting AP untuk melakukan Retur') return res
\ No newline at end of file diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml index 1893fcaf..562be8d9 100644 --- a/indoteknik_custom/views/stock_picking.xml +++ b/indoteknik_custom/views/stock_picking.xml @@ -56,6 +56,11 @@ string="Biteship" type="object" /> + <button name="track_envio_shipment" + string="Tracking Envio" + type="object" + attrs="{'invisible': [('carrier_id', '!=', 151)]}" + /> </button> <field name="backorder_id" position="after"> <field name="summary_qty_detail"/> @@ -143,6 +148,24 @@ <field name="sj_documentation" widget="image" /> <field name="paket_documentation" widget="image" /> </group> + <group> + <field name="envio_id" invisible="1"/> + <field name="envio_code"/> + <field name="envio_ref_code"/> + <field name="envio_eta_at"/> + <field name="envio_ata_at"/> + <field name="envio_etd_at" invisible="1"/> + <field name="envio_atd_at" invisible="1"/> + <field name="envio_received_by"/> + <field name="envio_status"/> + <field name="envio_cod_value" invisible="1"/> + <field name="envio_cod_status" invisible="1"/> + <field name="envio_latest_message"/> + <field name="envio_latest_recorded_at"/> + <field name="envio_latest_latitude" invisible="1"/> + <field name="envio_latest_longitude" invisible="1"/> + <field name="tracking_by" invisible="1"/> + </group> </group> </page> </page> |
