Beispiel #1
0
def sync_records(config, state, stream):
    write_schema(
        stream.tap_stream_id,
        stream.schema.to_dict(),
        stream.key_properties,
    )

    client = Client(config['subdomain'], config['api_key'])
    model_name = STREAM_MODEL_MAP[stream.tap_stream_id]
    model = client.model(model_name)
    domain = get_sync_domain(state, stream, model_name)
    sort_order = [
        ('write_date', 'asc'),
        ('create_date', 'asc'),
        ('id', 'asc'),
    ]

    # Get all fields defined in schema now
    fields = list(stream.schema.properties.keys())

    # Add create and write date to keep track of state
    fields.extend(['id', 'create_date', 'write_date'])
    for record in model.search_read_all(domain, sort_order, fields):
        transform(record)
        write_record(stream.tap_stream_id, record, time_extracted=utils.now())
        state = write_bookmark(state, stream.tap_stream_id, 'last_updated_at',
                               record['write_date'] or record['create_date'])
        state = write_bookmark(state, stream.tap_stream_id, 'last_record_id',
                               record['id'])
        write_state(state)
class TestFulfilClient(unittest.TestCase):
    def setUp(self):
        try:
            self.client = Client('fulfil_demo', os.environ['FULFIL_API_KEY'])
        except KeyError:
            self.fail('No FULFIL_API_KEY in environment')

    def tearDown(self):
        pass

    def test_000_connection(self):
        Model = self.client.model('ir.model')
        self.assertTrue(len(Model.search([])) > 0)

    def test_010_connection(self):
        Model = self.client.model('ir.model')
        with self.assertRaises(ServerError):
            Model.search([], context=1)
class TestFulfilClient(unittest.TestCase):

    def setUp(self):
        try:
            self.client = Client('fulfil_demo', os.environ['FULFIL_API_KEY'])
        except KeyError:
            self.fail('No FULFIL_API_KEY in environment')

    def tearDown(self):
        pass

    def test_000_connection(self):
        Model = self.client.model('ir.model')
        self.assertTrue(len(Model.search([])) > 0)
Beispiel #4
0
class Fulfil(object):
    """

    To get started you will wrap your application's app object something like
    this::

        app = Flask(__name__)
        fulfil = Fulfil(app)

    Configuration::

        FULFIL_SUBDOMAIN: the subdomain of your Fulfil account.
        FULFIL_API_KEY: The API_KEY to access Fulfil services.

    :param app: The Flask application object. Defaults to None.
    """
    client = None

    def __init__(self, app=None):
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        '''Initalizes the application with the extension.
        :param app: The Flask application object.
        '''
        offline_access_token = app.config.get('FULFIL_OFFLINE_ACCESS_TOKEN')
        if offline_access_token:
            self.client = Client(
                app.config['FULFIL_SUBDOMAIN'],
                auth=BearerAuth(offline_access_token),
                retry_on_rate_limit=True,
            )
        else:
            self.client = Client(
                app.config['FULFIL_SUBDOMAIN'],
                app.config['FULFIL_API_KEY'],
                retry_on_rate_limit=True,
            )
        app.jinja_env.filters['client_url'] = client_url

    def model(self, name):
        return self.client.model(name)

    def record(self, model_name, record_id):
        return self.client.record(model_name, record_id)
Beispiel #5
0
class Fulfil(object):
    """

    To get started you will wrap your application's app object something like
    this::

        app = Flask(__name__)
        fulfil = Fulfil(app)

    Configuration::

        FULFIL_SUBDOMAIN: the subdomain of your Fulfil account.
        FULFIL_API_KEY: The API_KEY to access Fulfil services.

    :param app: The Flask application object. Defaults to None.
    """
    client = None

    def __init__(self, app=None):
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        '''Initalizes the application with the extension.
        :param app: The Flask application object.
        '''
        offline_access_token = app.config.get('FULFIL_OFFLINE_ACCESS_TOKEN')
        if offline_access_token:
            self.client = Client(
                app.config['FULFIL_SUBDOMAIN'],
                auth=BearerAuth(offline_access_token)
            )
        else:
            self.client = Client(
                app.config['FULFIL_SUBDOMAIN'],
                app.config['FULFIL_API_KEY'],
            )
        app.jinja_env.filters['client_url'] = client_url

    def model(self, name):
        return self.client.model(name)

    def record(self, model_name, record_id):
        return self.client.record(model_name, record_id)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from decimal import Decimal

from fulfil_client import Client
client = Client('<subdomain>', '<api_key>')


# ==========================
# Creating Product Template
# ==========================

Template = client.model('product.template')

iphone, = Template.create([{
    'name': 'iPhone',
    'account_category': True,
}])

# =================
# Creating Products
# =================

Product = client.model('product.product')

iphone6, = Product.create([{
    'template': iphone['id'],
    'variant_name': 'iPhone 6',
    'code': 'IPHONE-6',
    'list_price': Decimal('699'),
    'cost_price': Decimal('599'),
Beispiel #7
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from fulfil_client import Client
client = Client('<subdomain>', '<api_key>')


# =============
# Creating Sale
# =============

# Sale requires customer(contact) and address id.
Contact = client.model('party.party')
Sale = client.model('sale.sale')

# Get the contact first
contacts = Contact.find([('name', 'ilike', '%Jon%')])
contact, = Contact.get(contacts[0]['id'])

sale, = Sale.create([{
    'party': contact['id'],
    'shipment_address': contact['addresses'][0],
    'invoice_address': contact['addresses'][0],
}])

# ===========================
# Adding items(line) to Sale
# ===========================

Product = client.model('product.product')
Line = client.model('sale.line')
Translate fulfil requests to curl.

Need to have the following installed

pip install curlify blinker
"""
import os
import curlify
from fulfil_client import Client
from fulfil_client.signals import response_received, signals_available

fulfil = Client(os.environ['FULFIL_SUBDOMAIN'], os.environ['FULFIL_API_KEY'])

print("Signal Available?:", signals_available)

Product = fulfil.model('product.product')

products = Product.find([])


@response_received.connect
def curlify_response(response):
    print('=' * 80)
    print(curlify.to_curl(response.request))
    print('=' * 80)
    print(response.content)
    print('=' * 80)


print Product.get_next_available_date(products[0]['id'], 1, 4, True)
Beispiel #9
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from fulfil_client import Client
client = Client('<subdomain>', '<api_key>')


# =================
# Creating Contacts
# =================

Contact = client.model('party.party')
contact, = Contact.create([{'name': 'Jon Doe'}])

# You can create multiple contacts too ;-)
contacts = Contact.create([
    {
        'name': 'Jon Doe'
    }, {
        'name': 'Matt Bower'
    }, {
        'name': 'Joe Blow'
    }
])

# ================
# Creating Address
# ================

# You need a contact id to create a address
Address = client.model('party.address')
Beispiel #10
0
def create_fullfill_order(item):
    item['service'] = 'ring reshaping'

    subdomain = 'aurate'
    # subdomain = 'aurate-sandbox'
    token = 'ee41ebf87f4a4fd29696f8b5db6b8cfc'
    # token = '43cf9ddb7acc4ac69586b8f1081d65ab'
    client = Client(subdomain, token)
    headers = {'X-API-KEY': token, 'Content-Type': 'application/json'}

    def get_fulfil_model_url(param):
        FULFIL_API_URL = f'https://{subdomain}.fulfil.io/api/v2'
        return f'{FULFIL_API_URL}/model/{param}'

    errors = []
    Model = client.model('sale.sale')
    sale = Model.search_read_all(
        domain=[["AND", ["reference", "=", item['order_name']]]],
        order=None,
        fields=['id', 'lines'],
        # batch_size=5,
    )
    DT = str(int(item['DT']))
    sale = sale.__next__()
    order_id = sale['id']

    Model = client.model('sale.line')
    f_lines = Model.search_read_all(
        domain=[["AND", ["id", "in", sale['lines']]]],
        order=None,
        fields=['product', 'product.code', 'quantity'],
    )
    f_lines = list(f_lines)
    # from .loopreturns import get_fulfil_model_url
    url = f'{get_fulfil_model_url("sale.sale")}/{order_id}/return_order'

    lines = []
    if True:
        ll = filter(lambda x: x['product.code'] == item['sku'], f_lines)
        line = ll.__next__()
        line_id = line['id']

        lines.append({
            "order_line_id": line_id,
            # Optional fields on line
            # ==================
            # "return_quantity": body_l[''],
            # defaults to the order line returnable quantity
            # "unit_price": "320.45",
            # defaults to the original order line unit price. Change this amount if the refund is not the full amount of the original order line.

            # If the return was created on an external returns platform,
            # the ID of the line
            "channel_identifier": DT,
            "note": "tracking_number " + item['tracking_number'],
            "return_reason": item['service'],  # Created if not exists
            'exchange_quantity': 1,
        })

    # if True:
    #     Model = client.model('product.product')
    #     # exchanges created through shopify
    #     product = Model.search_read_all(
    #         domain=[["AND", ["code", "=", item['sku']]]],
    #         order=None,
    #         fields=['id'],
    #     )
    #     product_id = product.__next__()['id']
    #     lines[i]['exchange_quantity'] = 1
    #     lines[i]['exchange_product'] = product_id
    #     lines[i]['exchange_unit_price'] = item['total']
    #     # # Exchange fields
    #     # # ==================
    #     # # +ve quantity of replacement item to ship to customer
    #     # "exchange_quantity": 1,
    #     # # ID of the product being sent.
    #     # # If replacement item is not specified, the same outbound item will be shipped.
    #     # "exchange_product": 1234,
    #     # # If the unit price is not specified, the unit price of the exchanged item is used.
    #     # "exchange_unit_price": "320.45",  # Unit price for outbound item

    if True:
        service_line = {
            "order_line_id": line_id,
            "return_quantity": 0,
            "note": item['service'],
            "exchange_quantity": 1,
            "exchange_product": SERVICE_SKU[item['service']],
        }
        lines.append(service_line)
    payload = [{
        "channel_identifier":
        DT,  # Unique identifier for the return in the channel. This will be used as idempotency key to avoid duplication.
        "reference":
        f'repair-{item["order_name"]}',  # Return order reference, RMA
        "lines": lines,
        "warehouse": 57,
    }]
    payload = dumps(payload)
    response = requests.put(url, data=payload, headers=headers)
    if response.status_code != 500:
        print("success")
    # if response.status_code != 200:
    #     content = f'''
    #     error response from fullfill: {response.status_code}<br/>
    #     text: {response.text}<br/>
    #     url {url}<br/>
    #     payload: <br/>
    #     {json.dumps(payload)}
    #     '''
    #     send_email("Loop webhook error!!!!", content, dev_recipients=True)

    return response.text, errors


#
#
# def create_fullfill_order_(item):
#     subdomain = 'aurate-sandbox'
#     token = '43cf9ddb7acc4ac69586b8f1081d65ab'
#     client = Client(subdomain, token)
#
#     CHANNEL_ID = 37
#
#     order_details = get_order_details(item)
#     SaleChannel = client.model('sale.channel')
#     record = SaleChannel.create_order(CHANNEL_ID, order_details)
#     print()
#
#
# def get_order_details(item):
#     a = item['address']
#     channel_identifier = str(int(item['DT']))
#     order_details = {
#         'channel_identifier': channel_identifier,
#         'reference': channel_identifier,
#         "confirmed_at": datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S.000+00:00'),
#         # "confirmed_at": "2021-05-11T08:20:23.251-05:00",
#         'customer': {
#             'name': a['name'],
#             'contacts': [
#                 ['email', item['email']]
#             ],
#         },
#
#         'billing_address': {
#             'name': a['name'],
#             'address1': a['address1'],
#             'address2': a['address2'],
#             'city': a['city'],
#             'zip': '50001',
#             'subdivision_code': a['province_code'],
#             'country_code': a['country_code'],
#             'email': item['email'],
#             'phone': a['phone'].replace('(', '').replace(')', '').replace('-', '').replace(' ', ''),
#         },
#         'shipping_address': {
#             'name': a['name'],
#             'address1': a['address1'],
#             'address2': a['address2'],
#             'city': a['city'],
#             'zip': '',
#             'subdivision_code': a['province_code'],
#             'country_code': a['country_code'],
#             'email': item['email'],
#             'phone': a['phone'].replace('(', '').replace(')', '').replace('-', '').replace(' ', ''),
#         },
#         'sale_lines': [
#             {
#                 'sku': item['sku'],
#                 'quantity': 1,
#                 'unit_price': Decimal('1.00'),
#                 'amount': Decimal('1.00'),
#                 'comment': 'Repearment'
#             },
#         ],
#         'shipping_lines': [
#         ],
#         'amount': Decimal('1.00'),
#         'currency_code': 'USD',
#         'payment_term': 'NET 30',
#         'priority': 2,
#         'status': 'pending',
#         'financial_status': 'paid',
#         'fulfillment_status': 'unshipped',
#     }
#     return order_details
#
#
#
#
#
#
# def create_fullfill_return_(item):
#     subdomain = 'aurate-sandbox'
#     token = '43cf9ddb7acc4ac69586b8f1081d65ab'
#     client = Client(subdomain, token)
#
#     CHANNEL_ID = 37
#     item()
#     order_details = get_order(item)
#     SaleChannel = client.model('sale.channel')
#     return_created(body)
#     print()
# def return_created(body):
#     errors = []
#     Model = client.model('sale.sale')
#     sale = Model.search_read_all(
#         domain=[["AND", ["reference", "=", body['order_name']]]],
#         order=None,
#         fields=['id', 'lines'],
#         # batch_size=5,
#     )
#     sale = list(sale)
#     if not sale:
#         errors.append(f"Can't create return, didn't find any sale with reference {body['order_name']}")
#         return errors
#     sale = sale[0]
#
#     Model = client.model('sale.line')
#     f_lines = Model.search_read_all(
#         domain=[["AND", ["id", "in", sale['lines']]]],
#         order=None,
#         fields=['product', 'product.code', 'quantity'],
#     )
#     f_lines = list(f_lines)
#     order_id = sale['id']
#     url = f'{get_fulfil_model_url("sale.sale")}/{order_id}/return_order'
#
#     lines = []
#     for body_l in body['line_items']:
#         ll = filter(lambda x: x['product.code'] == body_l['sku'], f_lines)
#         if not ll:
#             errors.append(f"Line not found {body_l}\n")
#             continue
#         line = ll.__next__()
#         line_id = line['id']
#
#         lines.append({
#             "order_line_id": line_id,
#             # Optional fields on line
#             # ==================
#             # "return_quantity": body_l[''],
#             # defaults to the order line returnable quantity
#             # "unit_price": "320.45",
#             # defaults to the original order line unit price. Change this amount if the refund is not the full amount of the original order line.
#
#             # If the return was created on an external returns platform,
#             # the ID of the line
#             "channel_identifier": body_l['line_item_id'],
#
#
#             # "note": "tracking_number " + body['tracking_number'],
#             "return_reason": body_l["return_reason"],  # Created if not exists
#         })
#     if not lines:
#         errors.append("Can't create return, didn't find any line")
#         return errors
#     if body['exchanges']:
#         Model = client.model('product.product')
#
#     # exchanges created through shopify
#     for i, item in enumerate(body['exchanges']):
#         if len(lines) > i:
#             product = Model.search_read_all(
#                 domain=[["AND", ["code", "=", item['sku']]]],
#                 order=None,
#                 fields=['id'],
#             )
#             product_id = product.__next__()['id']
#             lines[i]['exchange_quantity'] = 1
#             lines[i]['exchange_product'] = product_id
#             lines[i]['exchange_unit_price'] = item['total']
#             # # Exchange fields
#             # # ==================
#             # # +ve quantity of replacement item to ship to customer
#             # "exchange_quantity": 1,
#             # # ID of the product being sent.
#             # # If replacement item is not specified, the same outbound item will be shipped.
#             # "exchange_product": 1234,
#             # # If the unit price is not specified, the unit price of the exchanged item is used.
#             # "exchange_unit_price": "320.45",  # Unit price for outbound item
#         else:
#             errors.append(f"failed to add exchange for {item}\n "
#                           f"there is more exchanges than returns")
#             break
#     payload = [{
#             "channel_identifier":  body['id'],  # Unique identifier for the return in the channel. This will be used as idempotency key to avoid duplication.
#             "reference": body["order_name"],  # Return order reference, RMA
#             "lines": lines,
#             "warehouse": 140,
#         }]
#
#     response = requests.put(url, json=payload, headers=headers)
#
#     if response.status_code != 200:
#         content = f'''
#         error response from fullfill: {response.status_code}<br/>
#         text: {response.text}<br/>
#         url {url}<br/>
#         payload: <br/>
#         {json.dumps(payload)}
#         '''
#         send_email("Loop webhook error!!!!", content, dev_recipients=True)
#
#     return response.text, errors
from fulfil_client import Client

# Initializing Api
client = Client('indiraactive', '4b7b1a442e8a44e69e3c5d8ad8a64386')

# Initializing Fulfil's Product model
Product = client.model('product.product')

# Getting all the Product ids
products = Product.search([()])

# Making a write call on list of products to update "hs_code"
# Processing purchase orders in batches of 100
for i in range(0, len(products), 100):
    Product.write(products[i:i + 100], {'hs_code': '3926.20'})
Beispiel #12
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from decimal import Decimal

from fulfil_client import Client

client = Client('<subdomain>', '<api_key>')

# ==========================
# Creating Product Template
# ==========================

Template = client.model('product.template')

iphone, = Template.create([{
    'name': 'iPhone',
    'account_category': True,
}])

# =================
# Creating Products
# =================

Product = client.model('product.product')

iphone6, = Product.create([{
    'template': iphone['id'],
    'variant_name': 'iPhone 6',
    'code': 'IPHONE-6',
    'list_price': Decimal('699'),
    'cost_price': Decimal('599'),