feat: migrate suppliers from standalone app to ERPNext

This commit is contained in:
Rohan Bansal 2025-04-25 16:03:56 +05:30
parent af599910c0
commit 7b2811e8bd
12 changed files with 164 additions and 209 deletions

View file

@ -137,31 +137,30 @@ app_license = "mit"
# ---------------
# Hook on document methods and events
# doc_events = {
# "*": {
# "on_update": "method",
# "on_cancel": "method",
# "on_trash": "method"
# }
# }
doc_events = {
"Supplier": {
"autoname": "suppliers_portal.overrides.supplier.generate_supplier_id",
"after_insert": "suppliers_portal.overrides.supplier.create_user",
}
}
# Scheduled Tasks
# ---------------
scheduler_events = {
# "all": [
# "suppliers_portal.tasks.all"
# ],
"daily": ["suppliers_portal.utils.daily_invoice_status_update"],
# "hourly": [
# "suppliers_portal.tasks.hourly"
# ],
# "weekly": [
# "suppliers_portal.tasks.weekly"
# ],
# "monthly": [
# "suppliers_portal.tasks.monthly"
# ],
# "all": [
# "suppliers_portal.tasks.all"
# ],
"daily": ["suppliers_portal.utils.daily_invoice_status_update"],
# "hourly": [
# "suppliers_portal.tasks.hourly"
# ],
# "weekly": [
# "suppliers_portal.tasks.weekly"
# ],
# "monthly": [
# "suppliers_portal.tasks.monthly"
# ],
}
# Testing
@ -242,6 +241,6 @@ scheduler_events = {
website_route_rules = [
{"from_route": "/supplier-portal/<path:app_path>", "to_route": "supplier-portal"},
{"from_route": "/erpnext", "to_route": "/app"},
{"from_route": "/supplier-portal/<path:app_path>", "to_route": "supplier-portal"},
{"from_route": "/erpnext", "to_route": "/app"},
]

View file

@ -0,0 +1,43 @@
import random
import frappe
def generate_supplier_id(supplier, method=None):
length = 8
supplier_id = random.randint(10 ** (length - 1), 10**length - 1)
while frappe.db.exists("Supplier", {"supplier_id": supplier_id}):
supplier_id = random.randint(10 ** (length - 1), 10**length - 1)
supplier.name = str(supplier_id)
def create_user(supplier, method=None):
# skip user creation during patch migrations
if frappe.flags.in_patch:
return
# create role if it doesn't exist
if not frappe.db.exists("Role", "Portal Supplier"):
frappe.get_doc(
{
"doctype": "Role",
"role_name": "Portal Supplier",
"desk_access": False,
}
).insert()
user = frappe.new_doc("User")
user.update(
{
"email": f"{supplier.name}@supplier-portal.com",
"first_name": supplier.supplier_name,
"roles": [{"role": "Portal Supplier"}],
"new_password": supplier.name,
"send_welcome_email": False,
}
)
user.flags.ignore_password_policy = True
user.insert()
supplier.append("portal_users", {"user": user.name})
supplier.save()

View file

@ -3,4 +3,5 @@
# Read docs to understand patches: https://frappeframework.com/docs/v14/user/en/database-migrations
[post_model_sync]
# Patches added in this section will be executed after doctypes are migrated
# Patches added in this section will be executed after doctypes are migrated
suppliers_portal.patches.move_invoices_to_erpnext

View file

@ -0,0 +1,80 @@
from pathlib import Path
import frappe
from frappe.contacts.doctype.address.address import get_address_display
from frappe.core.doctype.data_import.importer import Importer, Row
from frappe.utils import update_progress_bar
# NOTE: This patch should be run on a fresh site with ERPNext installed
def execute():
import_suppliers()
def import_suppliers():
suppliers_file = Path(__file__).parent / "data" / "supplier.csv"
supplier_importer = Importer("Supplier", file_path=str(suppliers_file), console=True)
suppliers = supplier_importer.import_file.data
supplier: Row
frappe.flags.in_import = True
for idx, supplier in enumerate(suppliers):
update_progress_bar("Importing Suppliers", idx, (len(suppliers)))
(
_,
supplier_id,
created_on,
created_by,
company_name,
street,
city,
state,
pincode,
*extra,
) = supplier.as_list()
# ignore child rows
if not company_name:
continue
supplier_doc = frappe.new_doc("Supplier")
supplier_doc.update(
{
"name": supplier_id.replace('"', ""),
"supplier_name": company_name,
"owner": created_by,
"creation": created_on,
}
)
supplier_doc.insert(ignore_permissions=True)
if street:
address = frappe.new_doc("Address")
address.update(
{
"address_type": "Billing",
"address_title": company_name,
"address_line1": street,
"city": city or "Unknown",
"state": state,
"pincode": pincode,
"country": "United States",
"is_primary_address": True,
"is_shipping_address": True,
"links": [
{"link_doctype": "Supplier", "link_name": supplier_doc.name},
],
}
)
address.insert(ignore_permissions=True)
address_display = get_address_display(address.name)
supplier_doc.db_set("supplier_primary_address", address.name)
supplier_doc.db_set("primary_address", address_display)
frappe.flags.in_import = False
def import_invoices():
# invoices_file = Path(__file__).parent / "data" / "invoices.csv"
pass

View file

@ -2,35 +2,33 @@ import frappe
def execute():
update_supplier_invoices()
update_invoice_status()
update_supplier_invoices()
update_invoice_status()
def update_supplier_invoices():
suppliers = frappe.get_list("Supplier", pluck="name")
suppliers = frappe.get_list("Supplier", pluck="name")
for supplier in suppliers:
supplier_doc = frappe.get_doc("Supplier", supplier)
invoices = frappe.get_list(
"Invoices", filters={"supplier": supplier}, pluck="name"
)
if len(invoices) == 0:
continue
for supplier in suppliers:
supplier_doc = frappe.get_doc("Supplier", supplier)
invoices = frappe.get_list("Invoices", filters={"supplier": supplier}, pluck="name")
if len(invoices) == 0:
continue
supplier_doc.set("invoices", [])
for invoice in invoices:
supplier_doc.append("invoices", {"invoice_number": invoice})
supplier_doc.set("invoices", [])
for invoice in invoices:
supplier_doc.append("invoices", {"invoice_number": invoice})
supplier_doc.save()
supplier_doc.save()
frappe.db.commit()
frappe.db.commit()
def update_invoice_status():
invoices = frappe.get_list("Invoices", pluck="name")
invoices = frappe.get_list("Invoices", pluck="name")
for invoice in invoices:
invoice_doc = frappe.get_doc("Invoices", invoice)
invoice_doc.validate()
for invoice in invoices:
invoice_doc = frappe.get_doc("Invoices", invoice)
invoice_doc.validate()
frappe.db.commit()
frappe.db.commit()

View file

@ -1,8 +0,0 @@
// Copyright (c) 2024, jeowsome15@gmail.com and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Supplier", {
// refresh(frm) {
// },
// });

View file

@ -1,100 +0,0 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2024-09-24 02:39:16.353285",
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
"company_name",
"column_break_ykyo",
"company_address",
"user",
"street",
"city",
"state",
"zip_code",
"section_break_svzz",
"invoices"
],
"fields": [
{
"fieldname": "company_name",
"fieldtype": "Data",
"label": "Company Name"
},
{
"fieldname": "company_address",
"fieldtype": "Small Text",
"hidden": 1,
"label": "Company Address"
},
{
"fieldname": "user",
"fieldtype": "Link",
"hidden": 1,
"label": "User",
"options": "User"
},
{
"fieldname": "column_break_ykyo",
"fieldtype": "Column Break"
},
{
"fieldname": "street",
"fieldtype": "Data",
"label": "Street"
},
{
"fieldname": "city",
"fieldtype": "Data",
"label": "City"
},
{
"fieldname": "state",
"fieldtype": "Data",
"label": "State"
},
{
"fieldname": "zip_code",
"fieldtype": "Data",
"label": "Zip Code"
},
{
"fieldname": "section_break_svzz",
"fieldtype": "Section Break"
},
{
"fieldname": "invoices",
"fieldtype": "Table",
"label": "Invoices",
"options": "Supplier Invoices",
"read_only": 1
}
],
"links": [],
"modified": "2024-10-09 18:09:28.675273",
"modified_by": "Administrator",
"module": "Suppliers Portal",
"name": "Supplier",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"search_fields": "company_name",
"show_title_field_in_link": 1,
"sort_field": "creation",
"sort_order": "DESC",
"states": [],
"title_field": "company_name"
}

View file

@ -1,38 +0,0 @@
# Copyright (c) 2024, jeowsome15@gmail.com and contributors
# For license information, please see license.txt
import random
import frappe
from frappe.model.document import Document
class Supplier(Document):
def generate_supplier_id(self, length: int = 8) -> str:
# generate unique supplier integer id based on length
supplier_id = random.randint(10 ** (length - 1), 10**length - 1)
while frappe.db.exists("Supplier", {"supplier_id": supplier_id}):
supplier_id = random.randint(10 ** (length - 1), 10**length - 1)
return str(supplier_id)
def autoname(self):
self.name = self.generate_supplier_id()
def after_insert(self):
self.create_user()
def create_user(self):
user = frappe.new_doc("User")
user.update(
{
"email": f"{self.name}@supplier-portal.com",
"first_name": f"Supplier {self.name}",
"roles": [{"role": "Portal Supplier"}],
"new_password": self.name,
}
)
user.insert()
self.user = user.name
self.save()

View file

@ -1,7 +0,0 @@
{% extends "templates/web.html" %}
{% block page_content %}
<h1>{{ title |e }}</h1>
{% endblock %}
<!-- this is a sample default web page template -->

View file

@ -1,4 +0,0 @@
<div>
<a href="/{{ doc.route |e }}">{{ (doc.title or doc.name) |e }}</a>
</div>
<!-- this is a sample default list template -->

View file

@ -1,9 +0,0 @@
# Copyright (c) 2024, jeowsome15@gmail.com and Contributors
# See license.txt
# import frappe
from frappe.tests.utils import FrappeTestCase
class TestSupplier(FrappeTestCase):
pass