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

# -*- 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)