You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

320 lines
15 KiB

# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Swathy K S (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import base64
from datetime import datetime
from odoo import fields, models
from odoo.exceptions import UserError
from odoo.tools import json
import requests
class DeliveryCarrier(models.Model):
"""Creating a new delivery carrier and add new fields for
enter client credentials"""
_inherit = 'delivery.carrier'
delivery_type = fields.Selection(selection_add=[
('fedex_api', 'Fedex API')], ondelete={'fedex_api': 'set default'})
fedex_developer_key = fields.Char(string="Fedex Client ID",
help="Provide fedex developer key")
fedex_developer_pwd = fields.Char(string="Fedex Client Secret",
help="Secret key of fedex")
fedex_account_number = fields.Char(string="FedEx Account Number",
help="Fedex account number")
fedex_access_token = fields.Char(string="Fedex Access Token",
help="Access token for payload")
fedex_default_package_type_id = fields.Many2one('stock.package.type',
string="Fedex Package Type",
help="Package type")
fedex_service_type = fields.Selection(
[
('International_priority', 'International priority'),
('Fedex_International_priority', 'Fedex International priority'),
], default='Fedex_International_priority', string="Fedex Service type")
fedex_duty_payment = fields.Selection(
[('SENDER', 'Sender'), ('RECIPIENT', 'Recipient')], required=True,
default="SENDER", string="Fedex duty payment", help="Here we can set sender or receiver "
"who is responsible for duty of payment")
fedex_weight_unit = fields.Selection([('LB', 'LB'),
('KG', 'KG')],
default='LB', string="Select weight type")
fedex_label_stock_type = fields.Selection([('PAPER_LETTER', 'PAPER LETTER'),
('STOCK_4X6', 'STOCK 4X6')],
string='Label Type',
default='PAPER_LETTER')
fedex_label_file_type = fields.Selection([('PDF', 'PDF'),
('PNG', 'PNG'),
('ZPL', 'ZPL')],
default='PDF',
string="Fedex Label File Type")
price = fields.Float(string="Picking Id")
def fedex_api_rate_shipment(self, order):
"""Calculating fedex rate and add to order line"""
current_date = datetime.now().date()
if order:
if self.fedex_developer_key and self.fedex_developer_pwd and self.fedex_account_number:
user_details = order[0]['create_uid']
partner_details = order[0]['partner_id']
url = "https://apis-sandbox.fedex.com/oauth/token"
payload = {
"grant_type": "client_credentials",
"client_id": self.fedex_developer_key,
"client_secret": self.fedex_developer_pwd
}
headers = {
'Content-Type': "application/x-www-form-urlencoded",
}
response = requests.post(url, data=payload, headers=headers)
if response.status_code == 200:
json_response = response.json()
access_token = json_response.get('access_token')
self.fedex_access_token = access_token
if access_token:
url = "https://apis-sandbox.fedex.com/rate/v1/rates/quotes"
payload = {
"accountNumber": {
"value": self.fedex_account_number
},
"requestedShipment": {
"shipper": {
"address": {
"postalCode": str(user_details.zip),
"countryCode": str(user_details.country_id.code)
}
},
"recipient": {
"address": {
"postalCode": str(partner_details.zip),
"countryCode": str(partner_details.country_id.code)
}
},
"shipDateStamp": str(current_date),
"pickupType": "DROPOFF_AT_FEDEX_LOCATION",
"serviceType": self.fedex_service_type,
"packagingType": self.fedex_default_package_type_id.id,
"rateRequestType": [
"LIST",
"ACCOUNT"
],
"customsClearanceDetail": {
"dutiesPayment": {
"paymentType": "SENDER",
"payor": {
"responsibleParty": None
}
},
"commodities": [
{
"description": "",
"quantity": 500,
"quantityUnits": str(self.fedex_weight_unit),
"weight": {
"units": str(self.fedex_weight_unit),
"value": 10
},
"customsValue": {
"amount": 400,
"currency": str(self.env.company.currency_id.name)
}
}
]
},
"requestedPackageLineItems": [
{
"weight": {
"units": str(self.fedex_weight_unit),
"value": 10
}
}
]
}
}
data = json.dumps(payload)
headers = {
'Content-Type': "application/json",
'X-locale': "en_US",
'Authorization': f"Bearer {access_token}"
}
response = requests.post(url, data=data, headers=headers)
res = {}
if response.status_code == 200:
response_data = json.loads(response.text)
res['price'] = \
response_data['output']['rateReplyDetails'][0][
'ratedShipmentDetails'][0]['totalNetFedExCharge']
res['carrier_price'] = res['price']
res['success'] = True
res['warning_message'] = "Successfully added"
self.price = res['price']
return res
else:
raise UserError(("Please Enter valid credentials"))
else:
raise UserError(("Invalid credentials"))
def fedex_api_send_shipping(self, pickings):
"""The tracking number and exact price are passed to the main function
of send shipping"""
fedex_api_shipping = self.fedex_api_shipping(pickings)
stock_picking_data = self.env['stock.picking'].browse(pickings.id)
stock_picking_data.carrier_tracking_ref = fedex_api_shipping['tracking_number']
res = []
res = res + [{'exact_price': fedex_api_shipping['exact_price'],
'tracking_number': False}]
return res
def fedex_api_shipping(self, pickings):
"""Creating new request for ship api and get tracking number"""
current_date = datetime.now().date()
stock_picking_data = self.env['stock.picking'].browse(pickings.id)
sale_id = stock_picking_data[0]['sale_id']
partner_id_data = sale_id[0]['partner_id'][0]
user_id_data = sale_id[0]['create_uid'][0]
carrier_id_details = stock_picking_data[0]['carrier_id']
carrier_id = carrier_id_details.name
url = "https://apis-sandbox.fedex.com/ship/v1/shipments"
payload = {
"labelResponseOptions": "URL_ONLY",
"requestedShipment": {
"shipper": {
"contact": {
"personName": carrier_id,
"phoneNumber": 1234567890,
"companyName": carrier_id
},
"address": {
"streetLines": [
"demo"
],
"city": str(user_id_data),
"stateOrProvinceCode": "AR",
"postalCode": str(user_id_data.zip),
"countryCode": str(user_id_data.country_id.code),
}
},
"recipients": [
{
"contact": {
"personName": str(partner_id_data.name),
"phoneNumber": str(partner_id_data.mobile),
"companyName": str(partner_id_data.company_id.name)
},
"address": {
"streetLines": [
"RECIPIENT STREET LINE 1",
"RECIPIENT STREET LINE 2"
],
"city": str(partner_id_data.city),
"stateOrProvinceCode": "TN",
"postalCode": str(partner_id_data.zip),
"countryCode": str(partner_id_data.country_id.code)
}
}
],
"shipDatestamp": str(current_date),
"serviceType": str(self.fedex_service_type),
"packagingType": "fedex_box",
"pickupType": "DROPOFF_AT_FEDEX_LOCATION",
"blockInsightVisibility": "False",
"shippingChargesPayment": {
"paymentType": "SENDER"
},
"shipmentSpecialServices": {
"specialServiceTypes": [
"FEDEX_ONE_RATE"
]
},
"labelSpecification": {
"imageType": str(self.fedex_label_file_type),
"labelStockType": str(self.fedex_label_stock_type)
},
"requestedPackageLineItems": [
{}
]
},
"accountNumber": {
"value": self.fedex_account_number
}
}
headers = {
'Content-Type': "application/json",
'X-locale': "en_US",
'Authorization': f"Bearer {self.fedex_access_token}"
}
data = json.dumps(payload)
response = requests.post(url, data=data, headers=headers)
res = {}
if response.status_code == 200:
response_data = json.loads(response.text)
res['tracking_number'] = response_data["output"]["transactionShipments"][0][
"completedShipmentDetail"]["masterTrackingId"]["trackingNumber"]
res['exact_price'] = self.price
encoded_label = response_data['output']['transactionShipments'][0]['pieceResponses'][0][
'packageDocuments'][0]['encodedLabel']
if self.fedex_label_file_type == 'PDF':
mime_type = 'application/pdf'
name = "label.pdf"
elif self.fedex_label_file_type == 'PNG':
mime_type = 'image/png'
name = "label.png"
elif self.fedex_label_file_type == 'ZPLII':
mime_type = 'application/text'
name = "label.zpl"
else:
raise ValueError(
f"Unsupported fedex_label_file_type: {self.fedex_label_file_type}")
if isinstance(encoded_label, str):
encoded_label = encoded_label.encode('utf-8')
decoded_bytes = base64.b64decode(encoded_label)
decoded_txt = decoded_bytes.decode('utf-8')
decoded_text = decoded_txt.replace('^POI', '^PO')
url = "http://api.labelary.com/v1/printers/8dpmm/labels/4x6/0/"
headers = {
"Accept": mime_type
}
response = requests.post(url, headers=headers, data=decoded_text)
pdf_bytes = response.content
decoded_bytes = base64.b64encode(pdf_bytes)
if self.fedex_label_file_type == 'ZPLII':
decoded_byte = decoded_text.encode('utf-8')
decoded_bytes = base64.b64encode(decoded_byte)
attachment_values = {
'name': name,
'type': 'binary',
'datas': decoded_bytes,
'mimetype': mime_type,
'res_model': 'stock.picking',
'res_id': pickings.id,
}
attachment_id = self.env['ir.attachment'].sudo().create(attachment_values)
pickings.message_post(body="Delivery details", attachment_ids=[attachment_id.id])
return res
def fedex_api_get_tracking_link(self, picking):
"""Tracking button function for tracking the ship details"""
return "https://www.fedex.com/fedextrack/?action=track&trackingnumber=%s"% picking.carrier_tracking_ref