feat: migrate suppliers from standalone app to ERPNext
This commit is contained in:
parent
af599910c0
commit
7b2811e8bd
12 changed files with 164 additions and 209 deletions
|
|
@ -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"},
|
||||
]
|
||||
|
|
|
|||
43
suppliers_portal/overrides/supplier.py
Normal file
43
suppliers_portal/overrides/supplier.py
Normal 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()
|
||||
|
|
@ -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
|
||||
|
|
|
|||
80
suppliers_portal/patches/move_invoices_to_erpnext.py
Normal file
80
suppliers_portal/patches/move_invoices_to_erpnext.py
Normal 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
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
||||
// },
|
||||
// });
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
{% extends "templates/web.html" %}
|
||||
|
||||
{% block page_content %}
|
||||
<h1>{{ title |e }}</h1>
|
||||
{% endblock %}
|
||||
|
||||
<!-- this is a sample default web page template -->
|
||||
|
|
@ -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 -->
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue