mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 08:24:47 +00:00
chore: remove packlink, letmeship, and sendcloud files
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('LetMeShip', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-07-23 10:55:19.669830",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"api_id",
|
||||
"api_password"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "API ID",
|
||||
"read_only_depends_on": "eval:doc.enabled == 0"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_password",
|
||||
"fieldtype": "Password",
|
||||
"label": "API Password",
|
||||
"read_only_depends_on": "eval:doc.enabled == 0"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-21 10:28:37.607717",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "LetMeShip",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,386 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import requests
|
||||
import frappe
|
||||
import json
|
||||
import re
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.erpnext_integrations.utils import get_tracking_url
|
||||
|
||||
LETMESHIP_PROVIDER = 'LetMeShip'
|
||||
|
||||
class LetMeShip(Document):
|
||||
pass
|
||||
|
||||
def get_letmeship_available_services(delivery_to_type, pickup_address,
|
||||
delivery_address, shipment_parcel, description_of_content, pickup_date,
|
||||
value_of_goods, pickup_contact=None, delivery_contact=None):
|
||||
# Retrieve rates at LetMeShip from specification stated.
|
||||
api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id'])
|
||||
if not enabled or not api_id or not api_password:
|
||||
return []
|
||||
|
||||
set_letmeship_specific_fields(pickup_contact, delivery_contact)
|
||||
|
||||
# LetMeShip have limit of 30 characters for Company field
|
||||
if len(pickup_address.address_title) > 30:
|
||||
pickup_address.address_title = pickup_address.address_title[:30]
|
||||
if len(delivery_address.address_title) > 30:
|
||||
delivery_address.address_title = delivery_address.address_title[:30]
|
||||
parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content)
|
||||
|
||||
url = 'https://api.letmeship.com/v1/available'
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Origin': 'string'
|
||||
}
|
||||
payload = generate_payload(
|
||||
pickup_address=pickup_address,
|
||||
pickup_contact=pickup_contact,
|
||||
delivery_address=delivery_address,
|
||||
delivery_contact=delivery_contact,
|
||||
description_of_content=description_of_content,
|
||||
value_of_goods=value_of_goods,
|
||||
parcel_list=parcel_list,
|
||||
pickup_date=pickup_date
|
||||
)
|
||||
try:
|
||||
available_services = []
|
||||
response_data = requests.post(
|
||||
url=url,
|
||||
auth=(api_id, api_password),
|
||||
headers=headers,
|
||||
data=json.dumps(payload)
|
||||
)
|
||||
response_data = json.loads(response_data.text)
|
||||
if 'serviceList' in response_data:
|
||||
for response in response_data['serviceList']:
|
||||
available_service = frappe._dict()
|
||||
basic_info = response['baseServiceDetails']
|
||||
price_info = basic_info['priceInfo']
|
||||
available_service.service_provider = LETMESHIP_PROVIDER
|
||||
available_service.id = basic_info['id']
|
||||
available_service.carrier = basic_info['carrier']
|
||||
available_service.carrier_name = basic_info['name']
|
||||
available_service.service_name = ''
|
||||
available_service.is_preferred = 0
|
||||
available_service.real_weight = price_info['realWeight']
|
||||
available_service.total_price = price_info['netPrice']
|
||||
available_service.price_info = price_info
|
||||
available_services.append(available_service)
|
||||
return available_services
|
||||
else:
|
||||
frappe.throw(
|
||||
_('Error occurred while fetching LetMeShip prices: {0}')
|
||||
.format(response_data['message'])
|
||||
)
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred while fetching LetMeShip Prices: {0}')
|
||||
.format(str(exc)),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
return []
|
||||
|
||||
|
||||
def create_letmeship_shipment(pickup_address, delivery_address, shipment_parcel, description_of_content,
|
||||
pickup_date, value_of_goods, service_info, shipment_notific_email, tracking_notific_email,
|
||||
pickup_contact=None, delivery_contact=None):
|
||||
# Create a transaction at LetMeShip
|
||||
# LetMeShip have limit of 30 characters for Company field
|
||||
api_id, api_password, enabled = frappe.db.get_value('LetMeShip', 'LetMeShip', ['enabled', 'api_password', 'api_id'])
|
||||
if not enabled or not api_id or not api_password:
|
||||
return []
|
||||
|
||||
set_letmeship_specific_fields(pickup_contact, delivery_contact)
|
||||
|
||||
if len(pickup_address.address_title) > 30:
|
||||
pickup_address.address_title = pickup_address.address_title[:30]
|
||||
if len(delivery_address.address_title) > 30:
|
||||
delivery_address.address_title = delivery_address.address_title[:30]
|
||||
|
||||
parcel_list = get_parcel_list(json.loads(shipment_parcel), description_of_content)
|
||||
url = 'https://api.letmeship.com/v1/shipments'
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Origin': 'string'
|
||||
}
|
||||
payload = generate_payload(
|
||||
pickup_address=pickup_address,
|
||||
pickup_contact=pickup_contact,
|
||||
delivery_address=delivery_address,
|
||||
delivery_contact=delivery_contact,
|
||||
description_of_content=description_of_content,
|
||||
value_of_goods=value_of_goods,
|
||||
parcel_list=parcel_list,
|
||||
pickup_date=pickup_date,
|
||||
service_info=service_info,
|
||||
tracking_notific_email=tracking_notific_email,
|
||||
shipment_notific_email=shipment_notific_email
|
||||
)
|
||||
try:
|
||||
response_data = requests.post(
|
||||
url=url,
|
||||
auth=(api_id, api_password),
|
||||
headers=headers,
|
||||
data=json.dumps(payload)
|
||||
)
|
||||
response_data = json.loads(response_data.text)
|
||||
if 'shipmentId' in response_data:
|
||||
shipment_amount = response_data['service']['priceInfo']['totalPrice']
|
||||
awb_number = ''
|
||||
url = 'https://api.letmeship.com/v1/shipments/{id}'.format(id=response_data['shipmentId'])
|
||||
tracking_response = requests.get(url, auth=(api_id, api_password),headers=headers)
|
||||
tracking_response_data = json.loads(tracking_response.text)
|
||||
if 'trackingData' in tracking_response_data:
|
||||
for parcel in tracking_response_data['trackingData']['parcelList']:
|
||||
if 'awbNumber' in parcel:
|
||||
awb_number = parcel['awbNumber']
|
||||
return {
|
||||
'service_provider': LETMESHIP_PROVIDER,
|
||||
'shipment_id': response_data['shipmentId'],
|
||||
'carrier': service_info['carrier'],
|
||||
'carrier_service': service_info['service_name'],
|
||||
'shipment_amount': shipment_amount,
|
||||
'awb_number': awb_number,
|
||||
}
|
||||
elif 'message' in response_data:
|
||||
frappe.throw(
|
||||
_('Error occurred while creating Shipment: {0}')
|
||||
.format(response_data['message'])
|
||||
)
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred while creating Shipment: {0}')
|
||||
.format(str(exc)),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
|
||||
def generate_payload(
|
||||
pickup_address,
|
||||
pickup_contact,
|
||||
delivery_address,
|
||||
delivery_contact,
|
||||
description_of_content,
|
||||
value_of_goods,
|
||||
parcel_list,
|
||||
pickup_date,
|
||||
service_info=None,
|
||||
tracking_notific_email=None,
|
||||
shipment_notific_email=None
|
||||
):
|
||||
payload = {
|
||||
'pickupInfo': {
|
||||
'address': {
|
||||
'countryCode': pickup_address.country_code,
|
||||
'zip': pickup_address.pincode,
|
||||
'city': pickup_address.city,
|
||||
'street': pickup_address.address_line1,
|
||||
'addressInfo1': pickup_address.address_line2,
|
||||
'houseNo': '',
|
||||
},
|
||||
'company': pickup_address.address_title,
|
||||
'person': {
|
||||
'title': pickup_contact.title,
|
||||
'firstname': pickup_contact.first_name,
|
||||
'lastname': pickup_contact.last_name
|
||||
},
|
||||
'phone': {
|
||||
'phoneNumber': pickup_contact.phone,
|
||||
'phoneNumberPrefix': pickup_contact.phone_prefix
|
||||
},
|
||||
'email': pickup_contact.email,
|
||||
},
|
||||
'deliveryInfo': {
|
||||
'address': {
|
||||
'countryCode': delivery_address.country_code,
|
||||
'zip': delivery_address.pincode,
|
||||
'city': delivery_address.city,
|
||||
'street': delivery_address.address_line1,
|
||||
'addressInfo1': delivery_address.address_line2,
|
||||
'houseNo': '',
|
||||
},
|
||||
'company': delivery_address.address_title,
|
||||
'person': {
|
||||
'title': delivery_contact.title,
|
||||
'firstname': delivery_contact.first_name,
|
||||
'lastname': delivery_contact.last_name
|
||||
},
|
||||
'phone': {
|
||||
'phoneNumber': delivery_contact.phone,
|
||||
'phoneNumberPrefix': delivery_contact.phone_prefix
|
||||
},
|
||||
'email': delivery_contact.email,
|
||||
},
|
||||
'shipmentDetails': {
|
||||
'contentDescription': description_of_content,
|
||||
'shipmentType': 'PARCEL',
|
||||
'shipmentSettings': {
|
||||
'saturdayDelivery': False,
|
||||
'ddp': False,
|
||||
'insurance': False,
|
||||
'pickupOrder': False,
|
||||
'pickupTailLift': False,
|
||||
'deliveryTailLift': False,
|
||||
'holidayDelivery': False,
|
||||
},
|
||||
'goodsValue': value_of_goods,
|
||||
'parcelList': parcel_list,
|
||||
'pickupInterval': {
|
||||
'date': pickup_date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if service_info:
|
||||
payload['service'] = {
|
||||
'baseServiceDetails': {
|
||||
'id': service_info['id'],
|
||||
'name': service_info['service_name'],
|
||||
'carrier': service_info['carrier'],
|
||||
'priceInfo': service_info['price_info'],
|
||||
},
|
||||
'supportedExWorkType': [],
|
||||
'messages': [''],
|
||||
'description': '',
|
||||
'serviceInfo': '',
|
||||
}
|
||||
payload['shipmentNotification'] = {
|
||||
'trackingNotification': {
|
||||
'deliveryNotification': True,
|
||||
'problemNotification': True,
|
||||
'emails': [tracking_notific_email],
|
||||
'notificationText': '',
|
||||
},
|
||||
'recipientNotification': {
|
||||
'notificationText': '',
|
||||
'emails': [ shipment_notific_email ]
|
||||
}
|
||||
}
|
||||
payload['labelEmail'] = True
|
||||
return payload
|
||||
|
||||
def get_letmeship_label(shipment_id):
|
||||
try:
|
||||
# Retrieve shipment label from LetMeShip
|
||||
api_id = frappe.db.get_single_value('LetMeShip','api_id')
|
||||
api_password = frappe.db.get_single_value('LetMeShip','api_password')
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Origin': 'string'
|
||||
}
|
||||
url = 'https://api.letmeship.com/v1/shipments/{id}/documents?types=LABEL'\
|
||||
.format(id=shipment_id)
|
||||
shipment_label_response = requests.get(
|
||||
url,
|
||||
auth=(api_id,api_password),
|
||||
headers=headers
|
||||
)
|
||||
shipment_label_response_data = json.loads(shipment_label_response.text)
|
||||
if 'documents' in shipment_label_response_data:
|
||||
for label in shipment_label_response_data['documents']:
|
||||
if 'data' in label:
|
||||
return json.dumps(label['data'])
|
||||
else:
|
||||
frappe.throw(
|
||||
_('Error occurred while printing Shipment: {0}')
|
||||
.format(shipment_label_response_data['message'])
|
||||
)
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred while printing Shipment: {0}')
|
||||
.format(str(exc)),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
|
||||
|
||||
def get_letmeship_tracking_data(shipment_id):
|
||||
# return letmeship tracking data
|
||||
api_id = frappe.db.get_single_value('LetMeShip','api_id')
|
||||
api_password = frappe.db.get_single_value('LetMeShip','api_password')
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'Access-Control-Allow-Origin': 'string'
|
||||
}
|
||||
try:
|
||||
url = 'https://api.letmeship.com/v1/tracking?shipmentid={id}'.format(id=shipment_id)
|
||||
tracking_data_response = requests.get(
|
||||
url,
|
||||
auth=(api_id, api_password),
|
||||
headers=headers
|
||||
)
|
||||
tracking_data = json.loads(tracking_data_response.text)
|
||||
if 'awbNumber' in tracking_data:
|
||||
tracking_status = 'In Progress'
|
||||
if tracking_data['lmsTrackingStatus'].startswith('DELIVERED'):
|
||||
tracking_status = 'Delivered'
|
||||
if tracking_data['lmsTrackingStatus'] == 'RETURNED':
|
||||
tracking_status = 'Returned'
|
||||
if tracking_data['lmsTrackingStatus'] == 'LOST':
|
||||
tracking_status = 'Lost'
|
||||
tracking_url = get_tracking_url(
|
||||
carrier=tracking_data['carrier'],
|
||||
tracking_number=tracking_data['awbNumber']
|
||||
)
|
||||
return {
|
||||
'awb_number': tracking_data['awbNumber'],
|
||||
'tracking_status': tracking_status,
|
||||
'tracking_status_info': tracking_data['lmsTrackingStatus'],
|
||||
'tracking_url': tracking_url,
|
||||
}
|
||||
elif 'message' in tracking_data:
|
||||
frappe.throw(
|
||||
_('Error occurred while updating Shipment: {0}')
|
||||
.format(tracking_data['message'])
|
||||
)
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred while updating Shipment: {0}')
|
||||
.format(str(exc)),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
|
||||
|
||||
def get_parcel_list(shipment_parcel, description_of_content):
|
||||
parcel_list = []
|
||||
for parcel in shipment_parcel:
|
||||
formatted_parcel = {}
|
||||
formatted_parcel['height'] = parcel.get('height')
|
||||
formatted_parcel['width'] = parcel.get('width')
|
||||
formatted_parcel['length'] = parcel.get('length')
|
||||
formatted_parcel['weight'] = parcel.get('weight')
|
||||
formatted_parcel['quantity'] = parcel.get('count')
|
||||
formatted_parcel['contentDescription'] = description_of_content
|
||||
parcel_list.append(formatted_parcel)
|
||||
return parcel_list
|
||||
|
||||
def set_letmeship_specific_fields(pickup_contact, delivery_contact):
|
||||
pickup_contact.phone_prefix = pickup_contact.phone[:3]
|
||||
pickup_contact.phone = re.sub('[^A-Za-z0-9]+', '', pickup_contact.phone[3:])
|
||||
|
||||
pickup_contact.title = 'MS'
|
||||
if pickup_contact.gender == 'Male':
|
||||
pickup_contact.title = 'MR'
|
||||
|
||||
delivery_contact.phone_prefix = delivery_contact.phone[:3]
|
||||
delivery_contact.phone = re.sub('[^A-Za-z0-9]+', '', delivery_contact.phone[3:])
|
||||
|
||||
delivery_contact.title = 'MS'
|
||||
if delivery_contact.gender == 'Male':
|
||||
delivery_contact.title = 'MR'
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestLetMeShip(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Packlink', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@@ -1,48 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-07-22 10:45:17.672439",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"api_key"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_key",
|
||||
"fieldtype": "Data",
|
||||
"label": "API Key",
|
||||
"read_only_depends_on": "eval:doc.enabled == 0"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-05 16:33:59.720980",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "Packlink",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import frappe
|
||||
import requests
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from erpnext.erpnext_integrations.utils import get_tracking_url
|
||||
|
||||
PACKLINK_PROVIDER = 'Packlink'
|
||||
|
||||
class Packlink(Document):
|
||||
pass
|
||||
|
||||
def get_packlink_available_services(pickup_address, delivery_address, shipment_parcel,pickup_date):
|
||||
# Retrieve rates at PackLink from specification stated.
|
||||
from_zip = pickup_address.pincode
|
||||
from_country_code = pickup_address.country_code
|
||||
to_zip = delivery_address.pincode
|
||||
to_country_code = delivery_address.country_code
|
||||
shipment_parcel_params = ''
|
||||
parcel_list = packlink_get_parcel_list(json.loads(shipment_parcel))
|
||||
for (index, parcel) in enumerate(parcel_list):
|
||||
shipment_parcel_params += 'packages[{index}][height]={height}&packages[{index}][length]={length}&packages[{index}][weight]={weight}&packages[{index}][width]={width}&'.format(
|
||||
index=index,
|
||||
height=parcel['height'],
|
||||
length=parcel['length'],
|
||||
weight=parcel['weight'],
|
||||
width=parcel['width']
|
||||
)
|
||||
url = 'https://api.packlink.com/v1/services?from[country]={}&from[zip]={}&to[country]={}&to[zip]={}&{}sortBy=totalPrice&source=PRO'.format(
|
||||
from_country_code,
|
||||
from_zip,
|
||||
to_country_code,
|
||||
to_zip,
|
||||
shipment_parcel_params
|
||||
)
|
||||
api_key = frappe.db.get_single_value('Packlink', 'api_key')
|
||||
enabled = frappe.db.get_single_value('Packlink', 'enabled')
|
||||
if not api_key or not enabled:
|
||||
return []
|
||||
try:
|
||||
responses = requests.get(url, headers={'Authorization': api_key})
|
||||
responses_dict = json.loads(responses.text)
|
||||
# If an error occured on the api. Show the error message
|
||||
if 'messages' in responses_dict:
|
||||
frappe.msgprint(
|
||||
_('Packlink: {0}'
|
||||
.format(str(responses_dict['messages'][0]['message']))
|
||||
),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
available_services = []
|
||||
for response in responses_dict:
|
||||
if parse_pickup_date(pickup_date) \
|
||||
in response['available_dates'].keys():
|
||||
available_service = frappe._dict()
|
||||
available_service.service_provider = PACKLINK_PROVIDER
|
||||
available_service.carrier = response['carrier_name']
|
||||
available_service.carrier_name = response['name']
|
||||
available_service.service_name = ''
|
||||
available_service.is_preferred = 0
|
||||
available_service.total_price = response['price']['base_price']
|
||||
available_service.actual_price = response['price']['total_price']
|
||||
available_service.service_id = response['id']
|
||||
available_service.available_dates = response['available_dates']
|
||||
available_services.append(available_service)
|
||||
|
||||
return available_services
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred on Packlink: {0}')
|
||||
.format(str(exc)), indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
return []
|
||||
|
||||
|
||||
def create_packlink_shipment(pickup_address, delivery_address, shipment_parcel,
|
||||
description_of_content, pickup_date, value_of_goods, pickup_contact,
|
||||
delivery_contact, service_info):
|
||||
# Create a transaction at PackLink
|
||||
enabled = frappe.db.get_single_value('Packlink', 'enabled')
|
||||
if not enabled:
|
||||
frappe.throw(_('Packlink integration is not enabled'))
|
||||
api_key = frappe.db.get_single_value('Packlink', 'api_key')
|
||||
from_country_code = pickup_address.country_code
|
||||
to_country_code = delivery_address.country_code
|
||||
data = {
|
||||
'additional_data': {
|
||||
'postal_zone_id_from': '',
|
||||
'postal_zone_name_from': pickup_address.country,
|
||||
'postal_zone_id_to': '',
|
||||
'postal_zone_name_to': delivery_address.country,
|
||||
},
|
||||
'collection_date': parse_pickup_date(pickup_date),
|
||||
'collection_time': '',
|
||||
'content': description_of_content,
|
||||
'contentvalue': value_of_goods,
|
||||
'content_second_hand': False,
|
||||
'from': {
|
||||
'city': pickup_address.city,
|
||||
'company': pickup_address.address_title,
|
||||
'country': from_country_code,
|
||||
'email': pickup_contact.email,
|
||||
'name': pickup_contact.first_name,
|
||||
'phone': pickup_contact.phone,
|
||||
'state': pickup_address.country,
|
||||
'street1': pickup_address.address_line1,
|
||||
'street2': pickup_address.address_line2,
|
||||
'surname': pickup_contact.last_name,
|
||||
'zip_code': pickup_address.pincode,
|
||||
},
|
||||
'insurance': {'amount': 0, 'insurance_selected': False},
|
||||
'price': {},
|
||||
'packages': packlink_get_parcel_list(json.loads(shipment_parcel)),
|
||||
'service_id': service_info['service_id'],
|
||||
'to': {
|
||||
'city': delivery_address.city,
|
||||
'company': delivery_address.address_title,
|
||||
'country': to_country_code,
|
||||
'email': delivery_contact.email,
|
||||
'name': delivery_contact.first_name,
|
||||
'phone': delivery_contact.phone,
|
||||
'state': delivery_address.country,
|
||||
'street1': delivery_address.address_line1,
|
||||
'street2': delivery_address.address_line2,
|
||||
'surname': delivery_contact.last_name,
|
||||
'zip_code': delivery_address.pincode,
|
||||
},
|
||||
}
|
||||
|
||||
url = 'https://api.packlink.com/v1/shipments'
|
||||
headers = {
|
||||
'Authorization': api_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
try:
|
||||
response_data = requests.post(url, json=data, headers=headers)
|
||||
response_data = json.loads(response_data.text)
|
||||
if 'reference' in response_data:
|
||||
return {
|
||||
'service_provider': PACKLINK_PROVIDER,
|
||||
'shipment_id': response_data['reference'],
|
||||
'carrier': service_info['carrier'],
|
||||
'carrier_service': service_info['service_name'],
|
||||
'shipment_amount': service_info['actual_price'],
|
||||
'awb_number': '',
|
||||
}
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(
|
||||
_('Error occurred while creating Shipment: {0}')
|
||||
.format(str(exc)),
|
||||
indicator='orange',
|
||||
alert=True
|
||||
)
|
||||
|
||||
|
||||
def get_packlink_label(shipment_id):
|
||||
# Retrieve shipment label from PackLink
|
||||
enabled = frappe.db.get_single_value('Packlink', 'enabled')
|
||||
if not enabled:
|
||||
frappe.throw(_('Packlink integration is not enabled'))
|
||||
api_key = frappe.db.get_single_value('Packlink', 'api_key')
|
||||
headers = {
|
||||
'Authorization': api_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
shipment_label_response = requests.get(
|
||||
'https://api.packlink.com/v1/shipments/{id}/labels'.format(id=shipment_id),
|
||||
headers=headers
|
||||
)
|
||||
shipment_label = json.loads(shipment_label_response.text)
|
||||
if shipment_label:
|
||||
return shipment_label
|
||||
else:
|
||||
frappe.msgprint(_('Shipment ID not found'))
|
||||
|
||||
|
||||
def get_packlink_tracking_data(shipment_id):
|
||||
# Get Packlink Tracking Info
|
||||
enabled = frappe.db.get_single_value('Packlink', 'enabled')
|
||||
if not enabled:
|
||||
frappe.throw(_('Packlink integration is not enabled'))
|
||||
api_key = frappe.db.get_single_value('Packlink', 'api_key')
|
||||
headers = {
|
||||
'Authorization': api_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
try:
|
||||
url = 'https://api.packlink.com/v1/shipments/{id}'.format(id=shipment_id)
|
||||
tracking_data_response = requests.get(url, headers=headers)
|
||||
tracking_data = json.loads(tracking_data_response.text)
|
||||
if 'trackings' in tracking_data:
|
||||
tracking_status = 'In Progress'
|
||||
if tracking_data['state'] == 'DELIVERED':
|
||||
tracking_status = 'Delivered'
|
||||
if tracking_data['state'] == 'RETURNED':
|
||||
tracking_status = 'Returned'
|
||||
if tracking_data['state'] == 'LOST':
|
||||
tracking_status = 'Lost'
|
||||
awb_number = None if not tracking_data['trackings'] else tracking_data['trackings'][0]
|
||||
tracking_url = get_tracking_url(
|
||||
carrier=tracking_data['carrier'],
|
||||
tracking_number=awb_number
|
||||
)
|
||||
return {
|
||||
'awb_number': awb_number,
|
||||
'tracking_status': tracking_status,
|
||||
'tracking_status_info': tracking_data['state'],
|
||||
'tracking_url': tracking_url
|
||||
}
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(_('Error occurred while updating Shipment: {0}').format(
|
||||
str(exc)), indicator='orange', alert=True)
|
||||
return []
|
||||
|
||||
|
||||
def packlink_get_parcel_list(shipment_parcel):
|
||||
parcel_list = []
|
||||
for parcel in shipment_parcel:
|
||||
for count in range(parcel.get('count')):
|
||||
formatted_parcel = {}
|
||||
formatted_parcel['height'] = parcel.get('height')
|
||||
formatted_parcel['width'] = parcel.get('width')
|
||||
formatted_parcel['length'] = parcel.get('length')
|
||||
formatted_parcel['weight'] = parcel.get('weight')
|
||||
parcel_list.append(formatted_parcel)
|
||||
return parcel_list
|
||||
|
||||
|
||||
def parse_pickup_date(pickup_date):
|
||||
return pickup_date.replace('-', '/')
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestPacklink(unittest.TestCase):
|
||||
pass
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('SendCloud', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
@@ -1,56 +0,0 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-18 09:48:50.836233",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"enabled",
|
||||
"api_key",
|
||||
"api_secret"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_key",
|
||||
"fieldtype": "Data",
|
||||
"label": "API Key",
|
||||
"read_only_depends_on": "eval:doc.enabled == 0"
|
||||
},
|
||||
{
|
||||
"fieldname": "api_secret",
|
||||
"fieldtype": "Password",
|
||||
"label": "API Secret",
|
||||
"read_only_depends_on": "eval:doc.enabled == 0"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-21 10:28:57.710549",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "SendCloud",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import requests
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
SENDCLOUD_PROVIDER = 'SendCloud'
|
||||
|
||||
class SendCloud(Document):
|
||||
pass
|
||||
|
||||
def get_sendcloud_available_services(delivery_address, shipment_parcel):
|
||||
# Retrieve rates at SendCloud from specification stated.
|
||||
api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret'])
|
||||
if not enabled or not api_key or not api_secret:
|
||||
return []
|
||||
|
||||
try:
|
||||
url = 'https://panel.sendcloud.sc/api/v2/shipping_methods'
|
||||
responses = requests.get(url, auth=(api_key, api_secret))
|
||||
responses_dict = json.loads(responses.text)
|
||||
|
||||
available_services = []
|
||||
for service in responses_dict['shipping_methods']:
|
||||
for country in service['countries']:
|
||||
if country['iso_2'] == delivery_address.country_code:
|
||||
available_service = frappe._dict()
|
||||
available_service.service_provider = 'SendCloud'
|
||||
available_service.carrier = service['carrier']
|
||||
available_service.service_name = service['name']
|
||||
available_service.total_price = total_parcel_price(country['price'], json.loads(shipment_parcel))
|
||||
available_service.service_id = service['id']
|
||||
available_services.append(available_service)
|
||||
return available_services
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(_('Error occurred on SendCloud: {0}').format(
|
||||
str(exc)), indicator='orange', alert=True)
|
||||
|
||||
def create_sendcloud_shipment(
|
||||
shipment,
|
||||
delivery_address,
|
||||
delivery_contact,
|
||||
service_info,
|
||||
shipment_parcel,
|
||||
description_of_content,
|
||||
value_of_goods
|
||||
):
|
||||
# Create a transaction at SendCloud
|
||||
api_key, api_secret, enabled = frappe.db.get_value('SendCloud', 'SendCloud', ['enabled', 'api_key', 'api_secret'])
|
||||
if not enabled or not api_key or not api_secret:
|
||||
return []
|
||||
|
||||
parcels = []
|
||||
for i, parcel in enumerate(json.loads(shipment_parcel), start=1):
|
||||
parcel_data = {
|
||||
'name': "{} {}".format(delivery_contact.first_name, delivery_contact.last_name),
|
||||
'company_name': delivery_address.address_title,
|
||||
'address': delivery_address.address_line1,
|
||||
'address_2': delivery_address.address_line2 or '',
|
||||
'city': delivery_address.city,
|
||||
'postal_code': delivery_address.pincode,
|
||||
'telephone': delivery_contact.phone,
|
||||
'request_label': True,
|
||||
'email': delivery_contact.email,
|
||||
'data': [],
|
||||
'country': delivery_address.country_code,
|
||||
'shipment': {
|
||||
'id': service_info['service_id']
|
||||
},
|
||||
'order_number': "{}-{}".format(shipment, i),
|
||||
'external_reference': "{}-{}".format(shipment, i),
|
||||
'weight': parcel.get('weight'),
|
||||
'parcel_items': get_parcel_items(parcel, description_of_content, value_of_goods)
|
||||
}
|
||||
parcels.append(parcel_data)
|
||||
data = {
|
||||
'parcels': parcels
|
||||
}
|
||||
try:
|
||||
url = 'https://panel.sendcloud.sc/api/v2/parcels?errors=verbose'
|
||||
response_data = requests.post(url, json=data, auth=(api_key, api_secret))
|
||||
response_data = json.loads(response_data.text)
|
||||
if 'failed_parcels' in response_data:
|
||||
frappe.msgprint(_('Error occurred while creating Shipment: {0}'
|
||||
).format(response_data['failed_parcels'][0]['errors']), indicator='orange',
|
||||
alert=True)
|
||||
else:
|
||||
shipment_id = ', '.join([str(x['id']) for x in response_data['parcels']])
|
||||
awb_number = ', '.join([str(x['tracking_number']) for x in response_data['parcels']])
|
||||
return {
|
||||
'service_provider': 'SendCloud',
|
||||
'shipment_id': shipment_id,
|
||||
'carrier': service_info['carrier'],
|
||||
'carrier_service': service_info['service_name'],
|
||||
'shipment_amount': service_info['total_price'],
|
||||
'awb_number': awb_number
|
||||
}
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(_('Error occurred while creating Shipment: {0}').format(
|
||||
str(exc)), indicator='orange', alert=True)
|
||||
|
||||
def get_sendcloud_label(shipment_id):
|
||||
# Retrieve shipment label from SendCloud
|
||||
api_key = frappe.db.get_single_value('SendCloud', 'api_key')
|
||||
api_secret = frappe.db.get_single_value('SendCloud', 'api_secret')
|
||||
shipment_id_list = shipment_id.split(', ')
|
||||
label_urls = []
|
||||
for ship_id in shipment_id_list:
|
||||
shipment_label_response = \
|
||||
requests.get('https://panel.sendcloud.sc/api/v2/labels/{id}'.format(id=ship_id), auth=(api_key, api_secret))
|
||||
shipment_label = json.loads(shipment_label_response.text)
|
||||
label_urls.append(shipment_label['label']['label_printer'])
|
||||
if len(label_urls):
|
||||
return label_urls
|
||||
else:
|
||||
frappe.msgprint(_('Shipment ID not found'))
|
||||
|
||||
def get_sendcloud_tracking_data(shipment_id):
|
||||
# return SendCloud tracking data
|
||||
try:
|
||||
api_key = frappe.db.get_single_value('SendCloud', 'api_key')
|
||||
api_secret = frappe.db.get_single_value('SendCloud', 'api_secret')
|
||||
shipment_id_list = shipment_id.split(', ')
|
||||
awb_number = []
|
||||
tracking_status = []
|
||||
tracking_status_info = []
|
||||
tracking_urls = []
|
||||
for ship_id in shipment_id_list:
|
||||
tracking_data_response = \
|
||||
requests.get('https://panel.sendcloud.sc/api/v2/parcels/{id}'.format(id=ship_id), auth=(api_key, api_secret))
|
||||
tracking_data = json.loads(tracking_data_response.text)
|
||||
tracking_urls.append(tracking_data['parcel']['tracking_url'])
|
||||
awb_number.append(tracking_data['parcel']['tracking_number'])
|
||||
tracking_status.append(tracking_data['parcel']['status']['message'])
|
||||
tracking_status_info.append(tracking_data['parcel']['status']['message'])
|
||||
return {
|
||||
'awb_number': ', '.join(awb_number),
|
||||
'tracking_status': ', '.join(tracking_status),
|
||||
'tracking_status_info': ', '.join(tracking_status_info),
|
||||
'tracking_url': ', '.join(tracking_urls)
|
||||
}
|
||||
except Exception as exc:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
frappe.msgprint(_('Error occurred while updating Shipment: {0}').format(
|
||||
str(exc)), indicator='orange', alert=True)
|
||||
|
||||
def total_parcel_price(parcel_price, shipment_parcel):
|
||||
count = 0
|
||||
for parcel in shipment_parcel:
|
||||
count += parcel.get('count')
|
||||
return parcel_price * count
|
||||
|
||||
def get_parcel_items(parcel, description_of_content, value_of_goods):
|
||||
parcel_list = []
|
||||
formatted_parcel = {}
|
||||
formatted_parcel['description'] = description_of_content
|
||||
formatted_parcel['quantity'] = parcel.get('count')
|
||||
formatted_parcel['weight'] = parcel.get('weight')
|
||||
formatted_parcel['value'] = value_of_goods
|
||||
parcel_list.append(formatted_parcel)
|
||||
return parcel_list
|
||||
@@ -1,10 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestSendCloud(unittest.TestCase):
|
||||
pass
|
||||
Reference in New Issue
Block a user