Skip to content

Commit

Permalink
Merge branch 'sunday-dev' into shopify-returns
Browse files Browse the repository at this point in the history
  • Loading branch information
rtdany10 authored Feb 2, 2023
2 parents 81c6ec2 + a126df8 commit 64bb39f
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 57 deletions.
13 changes: 6 additions & 7 deletions .github/helper/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@ bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frapp
mkdir ~/frappe-bench/sites/test_site
cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/

mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"

mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"

mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES"

cd ~/frappe-bench || exit

Expand Down
4 changes: 2 additions & 2 deletions .github/helper/site_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"mail_password": "test",
"admin_password": "admin",
"root_login": "root",
"root_password": "travis",
"root_password": "root",
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"],
"install_apps": ["payments", "erpnext"],
"throttle_user_limit": 100
}
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
mysql:
image: mariadb:10.6
env:
MYSQL_ALLOW_EMPTY_PASSWORD: YES
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
Expand Down
1 change: 1 addition & 0 deletions ecommerce_integrations/shopify/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
SUPPLIER_ID_FIELD = "shopify_supplier_id"
ADDRESS_ID_FIELD = "shopify_address_id"
ORDER_ITEM_DISCOUNT_FIELD = "shopify_item_discount"
ITEM_SELLING_RATE_FIELD = "shopify_selling_rate"

# ERPNext already defines the default UOMs from Shopify but names are different
WEIGHT_TO_ERPNEXT_UOM_MAP = {"kg": "Kg", "g": "Gram", "oz": "Ounce", "lb": "Pound"}
10 changes: 10 additions & 0 deletions ecommerce_integrations/shopify/customer.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,18 @@ def _map_address_fields(shopify_address, customer_name, address_type, email):
"pincode": shopify_address.get("zip"),
"country": shopify_address.get("country"),
"email_id": email,
"gstin": shopify_address.get("company"),
}

if address_fields["gstin"]:
try:
from india_compliance.gst_india.utils import validate_gstin

address_fields["gstin"] = validate_gstin(address_fields["gstin"])
address_fields["gst_category"] = "Registered Regular"
except Exception:
address_fields.pop("gstin")

phone = shopify_address.get("phone")
if validate_phone_number(phone, throw=False):
address_fields["phone"] = phone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
"column_break_22",
"section_break_25",
"sales_order_series",
"column_break_27",
"sync_delivery_note",
"delivery_note_series",
"sync_sales_invoice",
"sales_invoice_series",
"column_break_27",
"sync_delivery_note",
"sync_sales_invoice_on_payment",
"sync_invoice_on_delivery",
"sync_invoice_on_order",
"section_break_22",
"html_16",
"taxes",
Expand Down Expand Up @@ -204,17 +206,11 @@
"mandatory_depends_on": "eval:doc.sync_delivery_note"
},
{
"default": "0",
"fieldname": "sync_sales_invoice",
"fieldtype": "Check",
"label": "Import Sales Invoice from Shopify if Payment is marked"
},
{
"depends_on": "eval:doc.sync_sales_invoice==1",
"depends_on": "eval:doc.sync_sales_invoice_on_payment || doc.sync_invoice_on_delivery || doc.sync_invoice_on_order",
"fieldname": "sales_invoice_series",
"fieldtype": "Select",
"label": "Sales Invoice Series",
"mandatory_depends_on": "eval:doc.sync_sales_invoice"
"mandatory_depends_on": "eval:doc.sync_sales_invoice_on_payment || doc.sync_invoice_on_delivery || doc.sync_invoice_on_order"
},
{
"fieldname": "section_break_22",
Expand Down Expand Up @@ -347,12 +343,30 @@
"fieldname": "sync_new_item_as_active",
"fieldtype": "Check",
"label": "Sync New Items as Active"
},
{
"default": "0",
"fieldname": "sync_sales_invoice_on_payment",
"fieldtype": "Check",
"label": "Import Sales Invoice from Shopify if Payment is marked"
},
{
"default": "0",
"fieldname": "sync_invoice_on_delivery",
"fieldtype": "Check",
"label": "Import Sales Invoice from Shopify if Fulfillment is created"
},
{
"default": "0",
"fieldname": "sync_invoice_on_order",
"fieldtype": "Check",
"label": "Import Sales Invoice from Shopify on Order creation"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-11-01 16:09:42.685577",
"modified": "2023-02-01 15:07:02.735741",
"modified_by": "Administrator",
"module": "shopify",
"name": "Shopify Setting",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
ADDRESS_ID_FIELD,
CUSTOMER_ID_FIELD,
FULLFILLMENT_ID_FIELD,
ITEM_SELLING_RATE_FIELD,
ORDER_ID_FIELD,
ORDER_ITEM_DISCOUNT_FIELD,
ORDER_NUMBER_FIELD,
Expand Down Expand Up @@ -113,6 +114,14 @@ def get_integration_to_erpnext_wh_mapping(self) -> Dict[IntegrationWarehouse, ER

def setup_custom_fields():
custom_fields = {
"Item": [
dict(
fieldname=ITEM_SELLING_RATE_FIELD,
label="Shopify Selling Rate",
fieldtype="Currency",
insert_after="standard_rate",
)
],
"Customer": [
dict(
fieldname=CUSTOMER_ID_FIELD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
ADDRESS_ID_FIELD,
CUSTOMER_ID_FIELD,
FULLFILLMENT_ID_FIELD,
ITEM_SELLING_RATE_FIELD,
ORDER_ID_FIELD,
ORDER_ITEM_DISCOUNT_FIELD,
ORDER_NUMBER_FIELD,
Expand Down Expand Up @@ -40,6 +41,7 @@ def test_custom_field_creation(self):
ADDRESS_ID_FIELD,
CUSTOMER_ID_FIELD,
FULLFILLMENT_ID_FIELD,
ITEM_SELLING_RATE_FIELD,
ORDER_ID_FIELD,
ORDER_NUMBER_FIELD,
ORDER_STATUS_FIELD,
Expand Down
54 changes: 49 additions & 5 deletions ecommerce_integrations/shopify/fulfillment.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import json

import frappe
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from frappe.utils import cint, cstr, getdate
from frappe.utils import cint, cstr, flt, getdate

from ecommerce_integrations.shopify.constants import (
FULLFILLMENT_ID_FIELD,
ORDER_ID_FIELD,
ORDER_NUMBER_FIELD,
SETTING_DOCTYPE,
)
from ecommerce_integrations.shopify.order import get_sales_order
from ecommerce_integrations.shopify.order import (
get_sales_order,
get_tax_account_description,
get_tax_account_head,
)
from ecommerce_integrations.shopify.product import get_item_code
from ecommerce_integrations.shopify.utils import create_shopify_log


Expand All @@ -34,6 +41,8 @@ def create_delivery_note(shopify_order, setting, so):
if not cint(setting.sync_delivery_note):
return

from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice

for fulfillment in shopify_order.get("fulfillments"):
if (
not frappe.db.get_value("Delivery Note", {FULLFILLMENT_ID_FIELD: fulfillment.get("id")}, "name")
Expand All @@ -50,18 +59,25 @@ def create_delivery_note(shopify_order, setting, so):
dn.items = get_fulfillment_items(
dn.items, fulfillment.get("line_items"), fulfillment.get("location_id")
)
dn.taxes = []
for tax in get_dn_taxes(fulfillment, setting):
dn.append("taxes", tax)
dn.flags.ignore_mandatory = True
dn.save()
dn.submit()

if shopify_order.get("note"):
dn.add_comment(text=f"Order Note: {shopify_order.get('note')}")

if setting.sync_invoice_on_delivery:
inv = make_sales_invoice(dn.name)
if inv.items:
setattr(inv, ORDER_ID_FIELD, fulfillment.get("order_id"))
setattr(inv, ORDER_NUMBER_FIELD, shopify_order.get("name"))
inv.submit()

def get_fulfillment_items(dn_items, fulfillment_items, location_id=None):
# local import to avoid circular imports
from ecommerce_integrations.shopify.product import get_item_code

def get_fulfillment_items(dn_items, fulfillment_items, location_id=None):
setting = frappe.get_cached_doc(SETTING_DOCTYPE)
wh_map = setting.get_integration_to_erpnext_wh_mapping()
warehouse = wh_map.get(str(location_id)) or setting.warehouse
Expand Down Expand Up @@ -105,3 +121,31 @@ def update_fulfillment_status(payload, request_id=None):
),
}
).insert(ignore_permissions=True)


def get_dn_taxes(fulfillment, setting):
taxes = []
line_items = fulfillment.get("line_items")

for line_item in line_items:
item_code = get_item_code(line_item)
for tax in line_item.get("tax_lines"):
tax_amt = (
flt(tax.get("rate", 0)) * flt(line_item.get("quantity", 0)) * flt(line_item.get("price", 0))
)
taxes.append(
{
"charge_type": "Actual",
"account_head": get_tax_account_head(tax),
"description": (
f"{get_tax_account_description(tax) or tax.get('title')} - {tax.get('rate') * 100.0:.2f}%"
),
"tax_amount": flt(tax_amt),
"included_in_print_rate": 0,
"cost_center": setting.cost_center,
"item_wise_tax_detail": json.dumps({item_code: [flt(tax.get("rate")) * 100, flt(tax_amt)]}),
"dont_recompute_tax": 1,
}
)

return taxes
70 changes: 49 additions & 21 deletions ecommerce_integrations/shopify/invoice.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,23 @@ def prepare_sales_invoice(payload, request_id=None):
try:
sales_order = get_sales_order(cstr(order["id"]))
if sales_order:
create_sales_invoice(order, setting, sales_order)
payment = order.get("payment_terms", {}).get("payment_schedules", [])
posting_date = getdate(payment[0]["completed_at"]) if payment else nowdate()
if cint(setting.sync_sales_invoice_on_payment):
create_sales_invoice(order, setting, sales_order, posting_date)
make_payment_entry_against_sales_invoice(cstr(order["id"]), setting, posting_date)
create_shopify_log(status="Success")
else:
create_shopify_log(status="Invalid", message="Sales Order not found for syncing sales invoice.")
except Exception as e:
create_shopify_log(status="Error", exception=e, rollback=True)


def create_sales_invoice(shopify_order, setting, so):
if (
not frappe.db.get_value("Sales Invoice", {ORDER_ID_FIELD: shopify_order.get("id")}, "name")
and so.docstatus == 1
and not so.per_billed
and cint(setting.sync_sales_invoice)
):

posting_date = getdate(shopify_order.get("created_at")) or nowdate()

def create_sales_invoice(shopify_order, setting, so, posting_date=nowdate()):
if so.docstatus == 1:
sales_invoice = make_sales_invoice(so.name, ignore_permissions=True)
if not sales_invoice.items:
return
sales_invoice.set(ORDER_ID_FIELD, str(shopify_order.get("id")))
sales_invoice.set(ORDER_NUMBER_FIELD, shopify_order.get("name"))
sales_invoice.set_posting_time = 1
Expand All @@ -51,8 +49,6 @@ def create_sales_invoice(shopify_order, setting, so):
set_cost_center(sales_invoice.items, setting.cost_center)
sales_invoice.insert(ignore_mandatory=True)
sales_invoice.submit()
if sales_invoice.grand_total > 0:
make_payament_entry_against_sales_invoice(sales_invoice, setting, posting_date)

if shopify_order.get("note"):
sales_invoice.add_comment(text=f"Order Note: {shopify_order.get('note')}")
Expand All @@ -63,13 +59,45 @@ def set_cost_center(items, cost_center):
item.cost_center = cost_center


def make_payament_entry_against_sales_invoice(doc, setting, posting_date=None):
def make_payment_entry_against_sales_invoice(order_id, setting, posting_date=None):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry

payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=setting.cash_bank_account)
payment_entry.flags.ignore_mandatory = True
payment_entry.reference_no = doc.name
payment_entry.posting_date = posting_date or nowdate()
payment_entry.reference_date = posting_date or nowdate()
payment_entry.insert(ignore_permissions=True)
payment_entry.submit()
invoices = frappe.db.get_all(
"Sales Invoice",
filters={ORDER_ID_FIELD: order_id, "docstatus": 1},
fields=["name", "due_date", "grand_total", "outstanding_amount"],
)

if not invoices:
frappe.throw(frappe._("Invoices not synced to mark payment."))

payment_entry = None

for inv in invoices:
if not payment_entry:
payment_entry = get_payment_entry(
"Sales Invoice", inv.name, bank_account=setting.cash_bank_account
)
continue

payment_entry.append(
"references",
{
"reference_doctype": "Sales Invoice",
"reference_name": inv.name,
"bill_no": "",
"due_date": inv.due_date,
"total_amount": inv.grand_total,
"outstanding_amount": inv.outstanding_amount,
"allocated_amount": inv.outstanding_amount,
},
)
payment_entry.paid_amount += inv.outstanding_amount

if payment_entry:
payment_entry.flags.ignore_mandatory = True
payment_entry.reference_no = order_id
payment_entry.posting_date = posting_date or nowdate()
payment_entry.reference_date = posting_date or nowdate()
payment_entry.insert(ignore_permissions=True)
payment_entry.submit()
6 changes: 4 additions & 2 deletions ecommerce_integrations/shopify/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ def create_order(order, setting, company=None):

so = create_sales_order(order, setting, company)
if so:
if order.get("financial_status") == "paid":
if cint(setting.sync_invoice_on_order):
create_sales_invoice(order, setting, so)
elif order.get("financial_status") == "paid" and cint(setting.sync_sales_invoice_on_payment):
create_sales_invoice(order, setting, so)

if order.get("fulfillments"):
Expand Down Expand Up @@ -211,7 +213,7 @@ def get_order_taxes(shopify_order, setting):

taxes = update_taxes_with_shipping_lines(
taxes,
shopify_order.get("shipping_lines"),
shopify_order.get("shipping_lines", []),
setting,
taxes_inclusive=shopify_order.get("taxes_included"),
)
Expand Down
Loading

0 comments on commit 64bb39f

Please sign in to comment.