Giter Club home page Giter Club logo

roulier's Introduction

Roulier

Roulier is a shipping library written in Python for sending parcels. Roulier will get a label + tracking number to your carrier for you.

big picture

  • Roulier runs on your server and call each carrier API directly.
  • You have to use your own credentials provided by each carriers.
  • Roulier is Open Source software, AGPL-3
  • Roulier integrate a multitude of carriers
    • French La Poste
    • French DPD
    • French GLS
    • French Chronopost
    • more to come... (geodis, kuehne...)

Installation

This is not compatible with python 2.7. Please use version < 1.0.0 (python2 branch) in that case.

Usage

from roulier import roulier

payload= {
	"auth": {
		"login": "12345",
		"password": "password",
	},
	"service": {
		"productCode": "COL"
	},
	"parcels": [{
		"weight": 3.4,
	}],
	"to_address": {
		"firstName": "Hparfr"
		"street1": "35 b Rue Montgolfier"
		"city": "Villeurbanne"
        "country": "FR",
        "zip": "69100"
   	},
   	"from_address": {
		"fristName": "Akretion France"
		"street1": "35 b Rue Montgolfier"
		"city": "Villeurbanne"
        "country": "FR",
        "zip": "69100"
   	},
}
# first parameter is the carrier type.
# second is the action and then the parameters needed by the action
response = roulier.get('laposte_fr', 'get_label', payload)


print(response)

Get supported carriers and related actions:

from roulier import roulier
print(roulier.get_carriers_action_available())

Known Issues

French GLS carrier :

  • The glsbox webservice only manage Basic products : BP, EBP, GBP
  • In the rest webservice, the incoterms don't work

Development

To release, increase the version number (in VERSION file) and create a tagged release on GitHub, with the same number. This will trigger publishing to PyPI.

Contributors

Dependencies

roulier's People

Contributors

bealdav avatar bguillot avatar christine-ho-dev avatar damdam-s avatar dylanncordel avatar florian-dacosta avatar hparfr avatar paradoxxxzero avatar sebastienbeau avatar tonygalmiche avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

roulier's Issues

Add dpd

  • basic implementation
  • use cerberus
  • output zpl
  • test
  • document

Remove firstname

Since firstname is not present some countries a simple name field is enough.
It will work with Laposte API and it's compliant with other API on the market.

pip install error

(env) USER@WORKSTATION:~$ pip install roulier==0.0.0
Collecting roulier==0.0.0
  Downloading roulier-0.0.0.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-4KXwP2/roulier/setup.py", line 6, in <module>
        version = open('VERSION').read().strip()
    IOError: [Errno 2] No such file or directory: 'VERSION'

I'm investigating but not so used to build pip installable package :(

Refactore roulier to be able to choose between multiple implementation for one carrier

For some carrier, we may want to manage different services/implementation.
Gls France for instance propose multiple solutions, like GLS-BOX, which return zpl, or GLS Web API.
Laposte fr also have multiple solutions (SOAP XML, the current implementation, or a REST API, not implemented yet)
DPD is also changing its api, the current one should be abandoned soon, etc...

It seems it could be quite usefull to ba able to choose among mulltipe implementation of a service/action.
For example to enjoy new functionalities or during the transition from one system to another, etc...

We currently have the case for GLS, where present implementation is the GLS-BOX and we want to also implementent the GLS Web API one : #138.

Since one carrier may have a propose different solution depending on the country (typlically, the implemented GLS-BOX is only for France and won't work for other countries), we had previously decided to add a suffix depending the country to be able to manage the carrier in multiple countries.
So, the gls_eu carrier type proposed in #138 is really confusing (unless it work for multiple european countries, but I doubt it..not sure though.

I'd like then to introduce a system allowing to choose between 2 implementation, while having a default one (so the user that do not care about which implementation is used to get a label don't need to care about that).

For this, we could modify the factory sytem to have something like that :

class RoulierFactory(object):
    def __init__(self):
        self._carrier_action = {}

    def register_builder(self, carrier_type, action, Carrierclass, ttype="default"):
        self._carrier_action[(carrier_type, action)] = {ttype: Carrierclass}

    def get(self, carrier_type, action, ttype="default, **kwargs):
        carrierclass = self._carrier_action.get((carrier_type, action), {}).get(ttype)
        if not carrierclass:
            raise ValueError((carrier_type, action))
        return carrierclass(carrier_type, action, **kwargs)

def get(carrier_type, action, *args, **kwargs):
    carrier_obj = factory.get(carrier_type, action)
    ttype = kwargs.pop('ttype', 'default')
    return getattr(carrier_obj, action)(carrier_type, action, *args, **kwargs)

And on carrier side :

class GlsFrBoxGetLabel(CarrierGetLabel):
...

factory.register_builder("gls_fr", "get_label", GlsFrBoxGetLabel, ttype="gls-box")

class GlsWebGetLabel(CarrierGetLabel):
...

factory.register_builder("gls_fr", "get_label", GlsWebGetLabel)

This way, calling roulier.get('gls_fr', 'get_label', payload) will return a gls label using the WEB api
and calling roulier.get('gls_fr', 'get_label', payload, ttype="gls-box") will return a gls label using the GLS-BOX solution.

Any comment about such a change ?
@DylannCordel I'd appreciate your opinion about this

cc @hparfr @bealdav

How to integrate this inside Odoo

Hi do you have tips on how to integrate this module inside Odoo.

I assume it's with your other module named delivery-carrier on branches 9-roulier, origin/9-roulier.
But how does it work when you installed delivery-carrier inside Odoo in v9 ?

Thx in advance,

Razgort

Traceback when use laposte api

When i want to use this api, i have a traceback

-> from roulier import roulier
-> laposte = roulier.get('laposte')
-> laposte.api()
Traceback (most recent call last):
File "", line 1, in
File "/root/roulier/roulier/carriers/laposte/laposte.py", line 19, in api
return self.encoder.api()
File "/root/roulier/roulier/carriers/laposte/laposte_encoder.py", line 52, in api
return api.api_values()
File "/root/roulier/roulier/api.py", line 158, in api_values
return self.normalize({})
File "/root/roulier/roulier/api.py", line 180, in normalize
return self._validator().normalized(data, self.api_schema())
File "/root/roulier/roulier/api.py", line 133, in api_schema
schemas = self._schemas()
File "/root/roulier/roulier/carriers/laposte/laposte_api.py", line 150, in _schemas
schemas = super(LaposteApi, self)._schemas()
File "/root/roulier/roulier/api.py", line 116, in _schemas
'parcels': self._parcels(),
File "/root/roulier/roulier/api.py", line 89, in _parcels
'default': [v.normalized({}, self._parcel())]
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 593, in normalized
self.__init_processing(document, schema)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 548, in __init_processing
self.schema = DefinitionSchema(self, schema)
File "/usr/local/lib/python3.5/dist-packages/cerberus/schema.py", line 69, in init
self.validate(schema)
File "/usr/local/lib/python3.5/dist-packages/cerberus/schema.py", line 197, in validate
self._validate(schema)
File "/usr/local/lib/python3.5/dist-packages/cerberus/schema.py", line 218, in _validate
if not self.schema_validator(schema, normalize=False):
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 879, in validate
self.__validate_unknown_fields(field)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 912, in __validate_unknown_fields
if not validator({field: value}, normalize=False):
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 877, in validate
self.__validate_definitions(definitions, field)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 940, in __validate_definitions
result = validate_rule(rule)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 922, in validate_rule
return validator(definitions.get(rule, None), field, value)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 1236, in _validate_schema
self.__validate_schema_mapping(field, schema, value)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 1247, in __validate_schema_mapping
if not validator(value, update=self.update, normalize=False):
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 877, in validate
self.__validate_definitions(definitions, field)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 940, in __validate_definitions
result = validate_rule(rule)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 921, in validate_rule
validator = self.__get_rule_handler('validate', rule)
File "/usr/local/lib/python3.5/dist-packages/cerberus/validator.py", line 338, in __get_rule_handler
"domain.".format(rule, domain))
RuntimeError: There's no handler for 'description' in the 'validate' domain.

I tried with python2.7 and python3.5 with the same result.
What i'm doing wrong ?

Cerberus 1.2

Hello,
I got an error when I call the api() method.
I'm on Cerberus 1.2. No issue with Cerberus 1.1.

Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from pprint import pprint
>>> from roulier import roulier
>>> 
>>> 
>>> laposte = roulier.get('laposte')
>>> pprint(laposte.api())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.linux-x86_64/egg/roulier/carriers/laposte/laposte.py", line 19, in api
  File "build/bdist.linux-x86_64/egg/roulier/carriers/laposte/laposte_encoder.py", line 52, in api
  File "build/bdist.linux-x86_64/egg/roulier/api.py", line 158, in api_values
  File "build/bdist.linux-x86_64/egg/roulier/api.py", line 180, in normalize
  File "build/bdist.linux-x86_64/egg/roulier/api.py", line 133, in api_schema
  File "build/bdist.linux-x86_64/egg/roulier/carriers/laposte/laposte_api.py", line 150, in _schemas
  File "build/bdist.linux-x86_64/egg/roulier/api.py", line 116, in _schemas
  File "build/bdist.linux-x86_64/egg/roulier/api.py", line 89, in _parcels
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 593, in normalized
    self.__init_processing(document, schema)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 548, in __init_processing
    self.schema = DefinitionSchema(self, schema)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/schema.py", line 69, in __init__
    self.validate(schema)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/schema.py", line 197, in validate
    self._validate(schema)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/schema.py", line 218, in _validate
    if not self.schema_validator(schema, normalize=False):
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 879, in validate
    self.__validate_unknown_fields(field)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 912, in __validate_unknown_fields
    if not validator({field: value}, normalize=False):
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 877, in validate
    self.__validate_definitions(definitions, field)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 940, in __validate_definitions
    result = validate_rule(rule)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 922, in validate_rule
    return validator(definitions.get(rule, None), field, value)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 1236, in _validate_schema
    self.__validate_schema_mapping(field, schema, value)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 1247, in __validate_schema_mapping
    if not validator(value, update=self.update, normalize=False):
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 877, in validate
    self.__validate_definitions(definitions, field)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 940, in __validate_definitions
    result = validate_rule(rule)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 921, in validate_rule
    validator = self.__get_rule_handler('validate', rule)
  File "/home/quentin/.local/lib/python2.7/site-packages/cerberus/validator.py", line 338, in __get_rule_handler
    "domain.".format(rule, domain))
RuntimeError: There's no handler for 'description' in the 'validate' domain.

Thank you

Geodis Response error

Geodis Response don't contain "attachement" key.

File "/workspace/parts/8-roulier/delivery_carrier_label_roulier/stock.py", line 219, in _call_roulier_api
ret = roulier_instance.get_label(payload)
File "/workspace/eggs/roulier-0.0.1-py2.7.egg/roulier/carriers/geodis/geodis.py", line 32, in get_label
return self.get(data, 'demandeImpressionEtiquette')
File "/workspace/eggs/roulier-0.0.1-py2.7.egg/roulier/carriers/geodis/geodis.py", line 26, in get
parts = response['attachement']
KeyError: 'attachement'

Difference between mobileNumber and phoneNumber

How could i make the difference between the mobileNumber and the phoneNumber with LaPoste since the template for an address is

<address>
        ...
	<mobileNumber>{{ address.phone }}</mobileNumber>
        ...
</address>

I'm thinking about making a pull request about that, is there anything I should know before?

Upgrade readme.md

  • Introduce what roulier is
  • big picture (py lib which call WS)
  • simple usage

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.