Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
422 changes: 422 additions & 0 deletions zort_connector/README.rst

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions zort_connector/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from . import models
33 changes: 33 additions & 0 deletions zort_connector/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright 2025 Ecosoft Co., Ltd (https://ecosoft.co.th)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "Zort Connector",
"summary": "Connects Odoo with Zort",
"version": "18.0.1.0.0",
"license": "AGPL-3",
"author": "Ecosoft, Odoo Community Association (OCA)",
"website": "https://github.com/ecosoft-odoo/ecosoft-addons",
"depends": ["stock", "sale_management", "usability_api_connector"],
"data": [
"security/res_groups.xml",
"security/ir.model.access.csv",
"data/ir_config_parameter_data.xml",
"data/api_config_data.xml",
"data/ir_cron_data.xml",
"data/ir_actions_server_data.xml",
"data/product_data.xml",
"data/partner_data.xml",
"views/res_config_settings_view.xml",
"views/zort_menu.xml",
"views/zort_sync_log_views.xml",
"views/zort_order_views.xml",
"views/zort_ecommerce_channel_views.xml",
"views/zort_product_view.xml",
"views/product_product_view.xml",
"views/sale_order_view.xml",
"views/stock_picking_view.xml",
],
"development_status": "Alpha",
"maintainers": ["TheerayutEncoder", "Saran440"],
}
198 changes: 198 additions & 0 deletions zort_connector/data/api_config_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<!-- Create Product -->
<record id="api_config_zort_create_product" model="api.config">
<field name="name">Zort Create Product</field>
<field name="code">zort_create_product</field>
<field name="model_id" ref="product.model_product_product" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Product/AddProduct</field>
<field name="method">post</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key" />
<field name="result_message_key">resDesc</field>
<field name="save_log">True</field>
<field name="python_code">{
"sku": rec.default_code,
"name": rec.name,
"sellprice": rec.lst_price,
"purchaseprice": rec.standard_price,
"unittext": rec.uom_name,
}</field>
</record>

<!-- Update Product -->
<record id="api_config_zort_update_product" model="api.config">
<field name="name">Zort Update Product</field>
<field name="code">zort_update_product</field>
<field name="model_id" ref="product.model_product_template" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Product/UpdateProduct</field>
<field name="method">post</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key">resCode</field>
<field name="result_success_value">200</field>
<field name="result_message_key">resDesc</field>
<field name="save_log">True</field>
<field
name="params_code"
>{"id": rec.zort_product_ids[:1].id_zort_product}</field>
<field name="python_code">{
"name": rec.name,
"sellprice": rec.list_price,
"purchaseprice": rec.standard_price,
"unittext": rec.uom_name,
# "weight": rec.weight,
# "sell_vat_status": 0,
# "purchase_vat_status": 0,
}</field>
</record>

<!-- Update Product Qty -->
<record id="api_config_zort_update_qty" model="api.config">
<field name="name">Zort Update Product Qty</field>
<field name="code">zort_update_qty</field>
<field name="model_id" ref="product.model_product_template" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Product/UpdateProductAvailableStockList</field>
<field name="method">post</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key">resCode</field>
<field name="result_success_value">200</field>
<field name="result_message_key">resDesc</field>
<field name="save_log">True</field>
<field
name="params_code"
>{"warehousecode": env.company.zort_warehouse_code or "W0001"}</field>
<field name="python_code">{
"stocks": [
{
"sku": rec.default_code,
"stock": rec.qty_available,
}
]
}</field>
</record>

<!-- Fetch Product Image -->
<record id="api_config_zort_fetch_product_image" model="api.config">
<field name="name">Zort Fetch Product Image</field>
<field name="code">zort_fetch_product_image</field>
<field name="model_id" ref="product.model_product_template" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Product/GetProducts</field>
<field name="method">get</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field
name="params_code"
>{"productidlist": rec.zort_product_ids[:1].id_zort_product}</field>
<field name="result_success_key" />
<field name="save_log">True</field>
</record>

<!-- Fetch Return Orders -->
<record id="api_config_zort_get_return_order" model="api.config">
<field name="name">Zort Get Return Orders</field>
<field name="code">zort_get_return_order</field>
<field name="model_id" ref="stock.model_stock_picking" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/ReturnOrder/GetReturnOrders</field>
<field name="method">get</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key" />
<field name="save_log">True</field>
<field name="params_code">{
"returnorderdateafter": env.context.get("zort_return_date_after", today_datetime.strftime("%Y-%m-%d")),
"returnorderdatebefore": today_datetime.strftime("%Y-%m-%d"),
}</field>
</record>

<!-- Sync Stock from Picking -->
<record id="api_config_zort_sync_stock_picking" model="api.config">
<field name="name">Zort Sync Stock from Picking</field>
<field name="code">zort_sync_stock_picking</field>
<field name="model_id" ref="stock.model_stock_picking" />
<field name="api_type">rest_api</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Product/UpdateProductAvailableStockList</field>
<field name="method">post</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key">resCode</field>
<field name="result_success_value">200</field>
<field name="result_message_key">resDesc</field>
<field name="save_log">True</field>
<field
name="params_code"
>{"warehousecode": env.company.zort_warehouse_code or "W0001"}</field>
<field name="python_code">{
"stocks": [
{"productid": zp.id_zort_product, "stock": move.product_id.qty_available}
for move in (rec.move_ids if rec.picking_type_code == "incoming" else rec.move_ids_without_package)
for zp in move.product_id.zort_product_ids
]
}</field>
</record>

<!-- Get Order -->
<record id="api_config_zort_get_order" model="api.config">
<field name="name">Zort Get Orders</field>
<field name="code">zort_get_order</field>
<field name="api_type">rest_api</field>
<field
name="description"
>Zort API credentials and endpoint configuration. Edit the Headers field to set storename, apikey, and apisecret.</field>
<field name="endpoint_system">external</field>
<field name="endpoint_url">https://open-api.zortout.com/v4</field>
<field name="route_path">/Order/GetOrders</field>
<field name="method">get</field>
<field name="auth_required">False</field>
<field name="headers">(lambda c: {
"storename": c.zort_store_name,
"apikey": c.zort_api_key,
"apisecret": c.zort_api_secret,
} if c.zort_connector_enabled else {})(env.company)</field>
<field name="result_success_key" />
<field name="save_log">False</field>
</record>
</odoo>
25 changes: 25 additions & 0 deletions zort_connector/data/ir_actions_server_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="action_update_qty_to_zort" model="ir.actions.server">
<field name="name">Update Quantity to Zort</field>
<field name="model_id" ref="stock.model_stock_picking" />
<field name="binding_model_id" ref="stock.model_stock_picking" />
<field name="binding_type">action</field>
<field name="state">code</field>
<field name="code">
# If update qty in stock.picking fail this allow user to manually trigger the update
if record.state == 'done' and not record.updated_qty_to_zort:
record.action_sync_qty_to_zort()
</field>
</record>

<record id="action_fetch_image_from_zort_product" model="ir.actions.server">
<field name="name">Update image from zort product</field>
<field name="model_id" ref="product.model_product_product" />
<field name="state">code</field>
<field name="code">
for product in records:
product.action_fetch_and_update_image_from_zort()
</field>
</record>
</odoo>
18 changes: 18 additions & 0 deletions zort_connector/data/ir_config_parameter_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="zort_connector_order_sync_days_back" model="ir.config_parameter">
<field name="key">zort_connector.order_sync_days_back</field>
<field name="value">10</field>
</record>
<!-- last_sync_datetime is intentionally left empty so the first run uses
order_sync_days_back as fallback. It is updated automatically after
each successful sync. -->
<record id="zort_connector_last_sync_datetime" model="ir.config_parameter">
<field name="key">zort_connector.last_sync_datetime</field>
<field name="value" />
</record>
<record id="zort_connector_pending_order_batch_size" model="ir.config_parameter">
<field name="key">zort_connector.pending_order_batch_size</field>
<field name="value">500</field>
</record>
</odoo>
39 changes: 39 additions & 0 deletions zort_connector/data/ir_cron_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="ir_cron_auto_sync_zort_order" model="ir.cron">
<field name="name">Auto Sync Zort Order</field>
<field name="model_id" ref="model_zort_order" />
<field name="state">code</field>
<field name="code">if env.company.zort_connector_enabled:
model.action_call_api("zort_get_order")</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
</record>
<record id="ir_cron_process_zort_orders_to_sale" model="ir.cron">
<field name="name">Process Zort Orders to Sale Orders</field>
<field name="model_id" ref="model_zort_order" />
<field name="state">code</field>
<field name="code">if env.company.zort_connector_enabled:
model.action_process_pending_orders()</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
</record>
<record id="ir_cron_validate_zort_sale_order" model="ir.cron">
<field name="name">Validate Zort - Sale Orders</field>
<field name="model_id" ref="model_sale_order" />
<field name="state">code</field>
<field name="code">if env.company.zort_connector_enabled:
model.action_validate_zort_order()</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
</record>
<record id="ir_cron_auto_sync_zort_return_order" model="ir.cron">
<field name="name">Auto Sync Zort Return Order</field>
<field name="model_id" ref="model_stock_picking" />
<field name="state">code</field>
<field name="code">if env.company.zort_connector_enabled:
model.action_create_return_picking()</field>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
</record>
</odoo>
23 changes: 23 additions & 0 deletions zort_connector/data/partner_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="marketplace_customer_1" model="res.partner">
<field name="name">Marketplace Customer</field>
<field name="is_company" eval="True" />
<field name="company_type">company</field>
</record>
<record id="marketplace_customer_2" model="res.partner">
<field name="name">Shopee Customer</field>
<field name="is_company" eval="True" />
<field name="company_type">company</field>
</record>
<record id="marketplace_customer_3" model="res.partner">
<field name="name">Lazada Customer</field>
<field name="is_company" eval="True" />
<field name="company_type">company</field>
</record>
<record id="marketplace_customer_4" model="res.partner">
<field name="name">Tiktok Customer</field>
<field name="is_company" eval="True" />
<field name="company_type">company</field>
</record>
</odoo>
30 changes: 30 additions & 0 deletions zort_connector/data/product_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="product_shipping_fee" model="product.template">
<field name="name">Shipping Fee</field>
<field name="type">service</field>
<field name="purchase_ok" eval="False" />
<field name="taxes_id" eval="False" />
<field name="list_price">0.0</field>
<field name="standard_price">0.0</field>
<field name="default_code">shipping_fee</field>
</record>
<record id="product_zort_discount" model="product.template">
<field name="name">Discount</field>
<field name="type">service</field>
<field name="purchase_ok" eval="False" />
<field name="taxes_id" eval="False" />
<field name="list_price">0.0</field>
<field name="standard_price">0.0</field>
<field name="default_code">zort_discount</field>
</record>
<record id="voucher_amount_zort" model="product.template">
<field name="name">Zort Voucher</field>
<field name="type">service</field>
<field name="purchase_ok" eval="False" />
<field name="taxes_id" eval="False" />
<field name="list_price">0.0</field>
<field name="standard_price">0.0</field>
<field name="default_code">zort_voucher</field>
</record>
</odoo>
11 changes: 11 additions & 0 deletions zort_connector/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).

from . import res_company
from . import res_config_settings
from . import ecommerce_channel
from . import zort_sync_log
from . import zort_order
from . import zort_product
from . import product_product
from . import sale_order
from . import stock_picking
Loading
Loading