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.
439 lines
22 KiB
439 lines
22 KiB
# -*- coding: utf-8 -*-
|
|
######################################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
#
|
|
# Copyright (C) 2020-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
|
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
|
|
#
|
|
# This program is under the terms of the Odoo Proprietary License v1.0 (OPL-1)
|
|
# It is forbidden to publish, distribute, sublicense, or sell copies of the Software
|
|
# or modified copies of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
# DEALINGS IN THE SOFTWARE.
|
|
#
|
|
########################################################################################
|
|
|
|
import json
|
|
from odoo import fields, models, api, _
|
|
from odoo.http import request
|
|
from odoo.exceptions import UserError
|
|
import requests
|
|
import base64
|
|
|
|
try:
|
|
from base64 import encodestring
|
|
except ImportError:
|
|
from base64 import encodebytes as encodestring
|
|
|
|
|
|
class DeliveryCarrier(models.Model):
|
|
_inherit = 'delivery.carrier'
|
|
|
|
delivery_type = fields.Selection(
|
|
selection_add=[('shipstation', 'ShipStation')],
|
|
ondelete={'shipstation': 'set '
|
|
'default'})
|
|
|
|
@api.onchange('shipstation_carrier')
|
|
def _onchange_shipstaion_carrier(self):
|
|
service_object = self.env['shipstation.service'].search(
|
|
[('carrier_code', '=', self.
|
|
shipstation_carrier.code)])
|
|
service_list = []
|
|
for data in service_object:
|
|
service_list.append(data.id)
|
|
package_object = self.env['shipstation.packages'].search(
|
|
[('carrier_code', '=', self.
|
|
shipstation_carrier.code)])
|
|
package_list = []
|
|
for data in package_object:
|
|
package_list.append(data.id)
|
|
res = {}
|
|
res['domain'] = {'shipstation_service': [('id', 'in', service_list)],
|
|
'shipstation_package': [('id', 'in', package_list)]}
|
|
return res
|
|
|
|
store = fields.Many2one('shipstation.store', string='Store',)
|
|
shipstation_carrier = fields.Many2one('shipstation.delivery',
|
|
string='Shipstation Carrier')
|
|
shipstation_service = fields.Many2one('shipstation.service',
|
|
string='Shipstation Delivery '
|
|
'Carrier Service')
|
|
shipstation_package = fields.Many2one('shipstation.packages',
|
|
string='Shipstation Package')
|
|
|
|
|
|
class ShipStationApi(models.Model):
|
|
_name = 'shipstation.api'
|
|
_description = "Shipstation Api"
|
|
_rec_name = 'host'
|
|
|
|
host = fields.Char(string="Host", help="Provide the api url",required=True)
|
|
username = fields.Char(string="Username", help="Provide the Shipstation API Key",required=True)
|
|
password = fields.Char(string="Password", help="Provide the Shipstation API Secret",required=True)
|
|
activate = fields.Boolean(string="Activate", default=False,
|
|
help="Make this credential active.Please activate only one credential ata time")
|
|
|
|
def get_carriers_service(self):
|
|
"""This function will create shipstation store,packages and other
|
|
information form shipstation"""
|
|
|
|
base64string = base64.encodebytes(('%s:%s' % (
|
|
self.username, self.password)).encode()).decode().replace('\n', '')
|
|
url = self.host + '/carriers'
|
|
headers = {
|
|
'Authorization': "Basic " + base64string
|
|
}
|
|
carriers = requests.get(url, headers=headers)
|
|
for rec in carriers.json():
|
|
# print (rec)
|
|
product = self.env['product.product'].search(
|
|
[('name', '=', 'ShipstationShipping')])
|
|
if not product:
|
|
product = self.env['product.product'].create({
|
|
'name': 'ShipstationShipping',
|
|
'type': 'service',
|
|
'lst_price': 0.0,
|
|
})
|
|
add_carriers = self.env['shipstation.delivery'].search([
|
|
('shipping_providerid', '=', rec.get('shippingProviderId'))])
|
|
if not add_carriers:
|
|
add_carriers = self.env['shipstation.delivery'].create({
|
|
'name': rec.get('name'),
|
|
'code': rec.get('code'),
|
|
'account_number': rec.get('accountNumber') or False,
|
|
'requires_funded_account': rec.get(
|
|
'requiresFundedAccount') or False,
|
|
'balance': rec.get('balance'),
|
|
'nick_name': rec.get('nickname'),
|
|
'shipping_providerid': rec.get('shippingProviderId'),
|
|
'primary': rec.get('primary')
|
|
})
|
|
|
|
url = self.host + '/carriers/listservices?carrierCode=' \
|
|
+ add_carriers.code
|
|
headers = {
|
|
'Authorization': "Basic " + base64string
|
|
}
|
|
services = requests.get(url, headers=headers)
|
|
for i in services.json():
|
|
services = self.env['shipstation.service'].search \
|
|
([('carrier_code', '=', i.get('carrier_code'))
|
|
and ('code', '=', i.get('code'))])
|
|
if not services:
|
|
services = self.env['shipstation.service'].create({
|
|
'carrier_code': i.get('carrierCode'),
|
|
'code': i.get('code'),
|
|
'name': i.get('name'),
|
|
'domestic': i.get('domestic'),
|
|
'international': i.get('international')
|
|
})
|
|
url = self.host + '/carriers/listpackages?carrierCode=' \
|
|
+ add_carriers.code
|
|
headers = {
|
|
'Authorization': "Basic " + base64string
|
|
}
|
|
packages = requests.get(url, headers=headers)
|
|
for p in packages.json():
|
|
package = self.env['shipstation.packages'].search \
|
|
([('carrier_code', '=', p.get('carrierCode'))
|
|
and ('code', '=', p.get('code')
|
|
and ('name', '=', p.get('name')))])
|
|
if not package:
|
|
package = self.env['shipstation.packages'].create({
|
|
'carrier_code': p.get('carrierCode'),
|
|
'code': p.get('code'),
|
|
'name': p.get('name'),
|
|
'domestic': p.get('domestic'),
|
|
'international': p.get('international')
|
|
})
|
|
|
|
url = self.host + '/stores'
|
|
headers = {
|
|
'Authorization': "Basic " + base64string
|
|
}
|
|
stores = requests.get(url, headers=headers)
|
|
for rec in stores.json():
|
|
store = self.env['shipstation.store'].search \
|
|
([('store_id', '=', rec.get('storeId')) and
|
|
('store_name', '=', rec.get('storeName'))])
|
|
if not store:
|
|
store = self.env['shipstation.store'].create({
|
|
'store_id': rec.get('storeId'),
|
|
'store_name': rec.get('storeName'),
|
|
'marketplace_name': rec.get('marketplaceName'),
|
|
'account_number': rec.get('accountName'),
|
|
'email': rec.get('email'),
|
|
'company_name': rec.get('companyName'),
|
|
'phone': rec.get('phone'),
|
|
'website': rec.get('website'),
|
|
'create_date': rec.get('createDate'),
|
|
'modified_date': rec.get('modifyDate')
|
|
})
|
|
|
|
|
|
class ChooseDeliveryCarrier(models.TransientModel):
|
|
_inherit = 'choose.delivery.carrier'
|
|
|
|
def _get_shipment_rate(self):
|
|
"""Calculating the shipping rate"""
|
|
vals = self.carrier_id.rate_shipment(self.order_id)
|
|
if vals.get('success'):
|
|
self.delivery_message = vals.get('warning_message', False)
|
|
self.delivery_price = vals['price']
|
|
self.display_price = vals['price']
|
|
return {}
|
|
return {'error_message': vals['error_message']}
|
|
|
|
|
|
class DeliveryCarrier(models.Model):
|
|
_inherit = ['delivery.carrier']
|
|
_primary_email = ['email_from']
|
|
|
|
code = fields.Char(string="Code")
|
|
|
|
def send_shipping(self, pickings):
|
|
self.ensure_one()
|
|
if hasattr(self, '%s_send_shipping' % self.delivery_type):
|
|
return getattr(self, '%s_send_shipping' % self.delivery_type)(
|
|
pickings)
|
|
|
|
@api.model
|
|
def create_oredr(self, pickings):
|
|
"""Creating order to shipstation"""
|
|
credential = self.env['shipstation.api'].search(
|
|
[('activate', '=', True)])
|
|
for rec in credential:
|
|
|
|
base64string = base64.encodebytes(('%s:%s' % (
|
|
rec.username, rec.password)).encode()).decode().replace('\n',
|
|
'')
|
|
if not pickings.shipstation_order_id or pickings.shipstation_shipping_cancel:
|
|
|
|
url = rec.host + '/orders/createorder'
|
|
else:
|
|
url = rec.host + '/orders/' + str(pickings.shipstation_order_id)
|
|
headers = {
|
|
'Host': 'ssapi.shipstation.com',
|
|
'Authorization': "Basic " + base64string,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
oredrNumber = pickings.origin
|
|
sale_oredr = self.env['sale.order'].search(
|
|
[('name', '=', pickings.origin)])
|
|
oredrDate = sale_oredr.date_order
|
|
paymentDate = sale_oredr.date_order
|
|
shipByDate = pickings.scheduled_date
|
|
if pickings.shipstation_shipping_cancel:
|
|
oredrStatus = 'cancelled'
|
|
|
|
else:
|
|
oredrStatus = 'awaiting_shipment'
|
|
customer_id = sale_oredr.partner_id.id
|
|
customerUsername = sale_oredr.partner_id.name
|
|
billname = sale_oredr.partner_id.name
|
|
bill_street1 = sale_oredr.partner_invoice_id.street
|
|
bill_street2 = sale_oredr.partner_invoice_id.street2 or ""
|
|
bill_city = sale_oredr.partner_invoice_id.city
|
|
bill_state = sale_oredr.partner_invoice_id.state_id.name
|
|
bill_postal = sale_oredr.partner_invoice_id.zip
|
|
bil_contry = sale_oredr.partner_invoice_id.country_id.code
|
|
bill_phone = sale_oredr.partner_invoice_id.phone
|
|
shipTo_name = sale_oredr.partner_id.name
|
|
shipTo_street1 = sale_oredr.partner_invoice_id.street
|
|
shipTo_street2 = sale_oredr.partner_invoice_id.street2 or " "
|
|
shipTo_city = sale_oredr.partner_invoice_id.city
|
|
shipTo_state = sale_oredr.partner_invoice_id.state_id.name
|
|
shipTo_postal = sale_oredr.partner_invoice_id.zip
|
|
shipTo_contry = sale_oredr.partner_invoice_id.country_id.code
|
|
|
|
shipTo_phone = sale_oredr.partner_invoice_id.phone
|
|
items = []
|
|
for rec in sale_oredr.order_line:
|
|
each = {"sku": rec.product_id.default_code or "null",
|
|
"name": rec.product_id.name,
|
|
"imageUrl": f'{request.httprequest.host_url}'
|
|
f'web/image?model=product.template&id='
|
|
f'{rec.product_id.id}&field=image_128',
|
|
"weight": {
|
|
"value": (rec.product_id.weight * 2.2046),
|
|
"units": "pounds"
|
|
},
|
|
"quantity": int(rec.product_uom_qty),
|
|
"unitPrice": rec.price_unit,
|
|
"taxAmount": rec.tax_id.amount,
|
|
"shippingAmount": (rec.price_unit * int(
|
|
rec.product_uom_qty)) + rec.tax_id.amount,
|
|
"productId": rec.product_id.id
|
|
}
|
|
items.append(each)
|
|
items = str(items)
|
|
|
|
amountPaid = sale_oredr.amount_untaxed
|
|
taxAmount = sale_oredr.amount_tax
|
|
shippingAmount = 0.0
|
|
customerNotes = sale_oredr.note
|
|
internalNotes = " "
|
|
paymentMethod = "Credit Card"
|
|
dc = self.env['delivery.carrier'].search([('name', '=',
|
|
pickings.carrier_id.name)])
|
|
requestedShippingService = dc.shipstation_service.name
|
|
carrierCode = dc.shipstation_carrier.code
|
|
serviceCode = dc.shipstation_service.code
|
|
packageCode = dc.shipstation_package.code
|
|
confirmation = "delivery"
|
|
shipDate = pickings.date_done
|
|
weight = "{\n\"value\": %s,\n\"units\":\"%s\"\n }" % (
|
|
(pickings.shipping_weight * 2.2046), "pounds")
|
|
tag_ids = sale_oredr.tag_ids
|
|
if pickings.shipstation_shipping_cancel and pickings.shipstation_shipping_orderKey:
|
|
|
|
payload = "{\n \"orderNumber\": \"%s\",\n \"orderKey\":\"%s\",\n \"orderDate\": \"%s\",\n \"paymentDate\": \"%s\",\n \"shipByDate\": \"%s\",\n \"orderStatus\": \"%s\",\n \"customerId\": \"%s\",\n \"customerUsername\": \"%s\",\n \"billTo\": {\n \"name\": \"%s\",\n \"street1\": \"%s\",\n \"street2\": \"%s\",\n \"city\": \"%s\",\n \"state\": \"%s\",\n \"postalCode\": \"%s\",\n \"country\": \"%s\",\n \"phone\": \"%s\" },\n \"shipTo\": {\n \"name\": \"%s\",\n \"street1\": \"%s\",\n \"street2\": \"%s\",\n \"city\": \"%s\",\n \"state\": \"%s\",\n \"postalCode\": \"%s\",\n \"country\":\"%s\",\n \"phone\": \"%s\" },\n \"items\":%s,\n \"amountPaid\": \"%s\",\n \"taxAmount\": \"%s\",\n \"shippingAmount\": \"%s\",\n \"customerNotes\": \"%s\",\n \"internalNotes\": \"%s\",\n \"paymentMethod\": \"%s\",\n \"requestedShippingService\": \"%s\",\n \"carrierCode\": \"%s\",\n \"serviceCode\":\"%s\",\n \"packageCode\": \"%s\",\n \"confirmation\": \"%s\",\n \"shipDate\": \"%s\",\n \"weight\":%s }" % (
|
|
oredrNumber, pickings.shipstation_shipping_orderKey,
|
|
oredrDate, paymentDate, shipByDate,
|
|
oredrStatus,
|
|
customer_id, customerUsername, billname, bill_street1,
|
|
bill_street2,
|
|
bill_city, bill_state, bill_postal, bil_contry, bill_phone,
|
|
shipTo_name,
|
|
shipTo_street1, shipTo_street2, shipTo_city, shipTo_state,
|
|
shipTo_postal, shipTo_contry, shipTo_phone, items,
|
|
amountPaid,
|
|
taxAmount,
|
|
shippingAmount, customerNotes, internalNotes, paymentMethod,
|
|
requestedShippingService, carrierCode, serviceCode,
|
|
packageCode,
|
|
confirmation, shipDate, weight)
|
|
else:
|
|
payload = "{\n \"orderNumber\": \"%s\",\n \"orderDate\": \"%s\",\n \"paymentDate\": \"%s\",\n \"shipByDate\": \"%s\",\n \"orderStatus\": \"%s\",\n \"customerId\": \"%s\",\n \"customerUsername\": \"%s\",\n \"billTo\": {\n \"name\": \"%s\",\n \"street1\": \"%s\",\n \"street2\": \"%s\",\n \"city\": \"%s\",\n \"state\": \"%s\",\n \"postalCode\": \"%s\",\n \"country\": \"%s\",\n \"phone\": \"%s\" },\n \"shipTo\": {\n \"name\": \"%s\",\n \"street1\": \"%s\",\n \"street2\": \"%s\",\n \"city\": \"%s\",\n \"state\": \"%s\",\n \"postalCode\": \"%s\",\n \"country\":\"%s\",\n \"phone\": \"%s\" },\n \"items\":%s,\n \"amountPaid\": \"%s\",\n \"taxAmount\": \"%s\",\n \"shippingAmount\": \"%s\",\n \"customerNotes\": \"%s\",\n \"internalNotes\": \"%s\",\n \"paymentMethod\": \"%s\",\n \"requestedShippingService\": \"%s\",\n \"carrierCode\": \"%s\",\n \"serviceCode\":\"%s\",\n \"packageCode\": \"%s\",\n \"confirmation\": \"%s\",\n \"shipDate\": \"%s\",\n \"weight\":%s }" % (
|
|
oredrNumber, oredrDate, paymentDate, shipByDate,
|
|
oredrStatus,
|
|
customer_id, customerUsername, billname, bill_street1,
|
|
bill_street2,
|
|
bill_city, bill_state, bill_postal, bil_contry, bill_phone,
|
|
shipTo_name,
|
|
shipTo_street1, shipTo_street2, shipTo_city, shipTo_state,
|
|
shipTo_postal, shipTo_contry, shipTo_phone, items,
|
|
amountPaid,
|
|
taxAmount,
|
|
shippingAmount, customerNotes, internalNotes, paymentMethod,
|
|
requestedShippingService, carrierCode, serviceCode,
|
|
packageCode,
|
|
confirmation, shipDate, weight)
|
|
|
|
create_order = requests.request("POST", url, headers=headers,
|
|
data=payload)
|
|
result = create_order.json()
|
|
|
|
if not pickings.shipstation_order_id and result.get('orderId'):
|
|
pickings.shipstation_order_id = result.get('orderId')
|
|
pickings.carrier_tracking_ref = result.get('orderId')
|
|
pickings.shipstation_shipping_orderKey = result.get('orderKey')
|
|
attachment_64 = pickings.get_label()
|
|
|
|
so_attachment = self.env['ir.attachment'].sudo().create({
|
|
'name': 'Shipstation Shipping Label - %s.pdf' % attachment_64.get('trackingNumber'),
|
|
'type': 'binary',
|
|
'mimetype': 'application/pdf',
|
|
'datas': attachment_64.get('data')
|
|
})
|
|
pickings.message_post(
|
|
body=(_("Order created into Shipstation for %s<br/>" % self.name)),
|
|
attachment_ids=[so_attachment.id]
|
|
)
|
|
|
|
def shipstation_cancel_shipment(self, pickings):
|
|
"""Function to cancel shipstaion order"""
|
|
pickings.shipstation_shipping_cancel = True
|
|
pickings.send_to_shipper()
|
|
|
|
def shipstation_send_shipping(self, pickings):
|
|
res = []
|
|
self.create_oredr(pickings)
|
|
for p in pickings:
|
|
res = res + [{'exact_price': p.carrier_id.fixed_price,
|
|
'tracking_number': False}]
|
|
return res
|
|
|
|
def shipstation_rate_shipment(self, order):
|
|
"""Calculating shipping rate"""
|
|
|
|
shipstation = self.env['shipstation.api'].search(
|
|
[('activate', '=', True)], limit=1)
|
|
|
|
base64string = base64.encodebytes(('%s:%s' % (
|
|
shipstation.username,
|
|
shipstation.password)).encode()).decode().replace('\n', '')
|
|
|
|
url = shipstation.host + "/shipments/getrates"
|
|
company_postal = order.company_id.zip
|
|
to_state = order.partner_shipping_id.state_id.name
|
|
to_country = order.partner_shipping_id.country_id.code
|
|
to_postal = order.partner_shipping_id.zip
|
|
to_city = order.partner_shipping_id.city
|
|
weight_value = 0
|
|
for rec in order.order_line:
|
|
weight_value = weight_value + (
|
|
rec.product_id.weight * rec.product_uom_qty)
|
|
payload = "{\n \"carrierCode\": \"%s\",\n \"serviceCode\": \"%s\",\n \"packageCode\": \"%s\",\n \"fromPostalCode\": \"%s\",\n \"toState\": \"%s\",\n \"toCountry\": \"%s\",\n \"toPostalCode\": \"%s\",\n \"toCity\": \"%s\",\n \"weight\": {\n \"value\": %s,\n \"units\": \"pounds\"\n }\n}" % (
|
|
self.shipstation_carrier.code, self.shipstation_service.code,
|
|
self.shipstation_package.code, company_postal, to_state, to_country,
|
|
to_postal, to_city, (weight_value * 2.2046))
|
|
headers = {
|
|
'Host': 'ssapi.shipstation.com',
|
|
'Authorization': "Basic " + base64string,
|
|
'Content-Type': 'application/json'
|
|
}
|
|
response = requests.request("POST", url, headers=headers, data=payload)
|
|
result = response.json()
|
|
# print("result", result)
|
|
if not result:
|
|
|
|
final = {'success': True,
|
|
'price': 0.0,
|
|
'error_message': True,
|
|
'warning_message': "Something went wrong"}
|
|
else:
|
|
if 'shipmentCost' in result[0]:
|
|
price = result[0].get('shipmentCost')
|
|
final = {'success': True,
|
|
'price': price,
|
|
'error_message': False,
|
|
'warning_message': False}
|
|
else:
|
|
final = {'success': True,
|
|
'price': 0,
|
|
'error_message': True,
|
|
'warning_message': result.get('ExceptionMessage')}
|
|
return final
|
|
|
|
def rate_shipment(self, order):
|
|
self.ensure_one()
|
|
if hasattr(self, '%s_rate_shipment' % self.delivery_type):
|
|
res = getattr(self, '%s_rate_shipment' % self.delivery_type)(order)
|
|
# apply margin on computed price
|
|
res['price'] = float(res['price']) * (
|
|
1.0 + (float(self.margin) / 100.0))
|
|
# free when order is large enough
|
|
if res['success'] and self.free_over and \
|
|
order._compute_amount_total_without_delivery() >= self.amount:
|
|
res['warning_message'] = _(
|
|
'Info:\nThe shipping is free because the order amount '
|
|
'exceeds %.2f.\n(The actual shipping cost is: %.2f)') % (
|
|
self.amount, res['price'])
|
|
res['price'] = 0.0
|
|
return res
|
|
|
|
def cancel_shipment(self, pickings):
|
|
|
|
self.ensure_one()
|
|
if hasattr(self, '%s_cancel_shipment' % self.delivery_type):
|
|
return getattr(self, '%s_cancel_shipment' % self.delivery_type)(
|
|
pickings)
|
|
|