From 7b2811e8bd30caa6bf11766b475d55d06dd51044 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 25 Apr 2025 16:03:56 +0530 Subject: [PATCH 1/8] feat: migrate suppliers from standalone app to ERPNext --- suppliers_portal/hooks.py | 43 ++++---- suppliers_portal/overrides/supplier.py | 43 ++++++++ suppliers_portal/patches.txt | 3 +- .../patches/move_invoices_to_erpnext.py | 80 ++++++++++++++ suppliers_portal/patches/oct_12.py | 38 ++++--- .../doctype/supplier/__init__.py | 0 .../doctype/supplier/supplier.js | 8 -- .../doctype/supplier/supplier.json | 100 ------------------ .../doctype/supplier/supplier.py | 38 ------- .../supplier/templates/portal_supplier.html | 7 -- .../templates/portal_supplier_row.html | 4 - .../doctype/supplier/test_supplier.py | 9 -- 12 files changed, 164 insertions(+), 209 deletions(-) create mode 100644 suppliers_portal/overrides/supplier.py create mode 100644 suppliers_portal/patches/move_invoices_to_erpnext.py delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/__init__.py delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/supplier.js delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/supplier.json delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/supplier.py delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier.html delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier_row.html delete mode 100644 suppliers_portal/suppliers_portal/doctype/supplier/test_supplier.py diff --git a/suppliers_portal/hooks.py b/suppliers_portal/hooks.py index 099d4a4..6318abe 100644 --- a/suppliers_portal/hooks.py +++ b/suppliers_portal/hooks.py @@ -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/", "to_route": "supplier-portal"}, - {"from_route": "/erpnext", "to_route": "/app"}, + {"from_route": "/supplier-portal/", "to_route": "supplier-portal"}, + {"from_route": "/erpnext", "to_route": "/app"}, ] diff --git a/suppliers_portal/overrides/supplier.py b/suppliers_portal/overrides/supplier.py new file mode 100644 index 0000000..df49a41 --- /dev/null +++ b/suppliers_portal/overrides/supplier.py @@ -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() diff --git a/suppliers_portal/patches.txt b/suppliers_portal/patches.txt index f15c3a9..e50c7b1 100644 --- a/suppliers_portal/patches.txt +++ b/suppliers_portal/patches.txt @@ -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 \ No newline at end of file +# Patches added in this section will be executed after doctypes are migrated +suppliers_portal.patches.move_invoices_to_erpnext diff --git a/suppliers_portal/patches/move_invoices_to_erpnext.py b/suppliers_portal/patches/move_invoices_to_erpnext.py new file mode 100644 index 0000000..2298f02 --- /dev/null +++ b/suppliers_portal/patches/move_invoices_to_erpnext.py @@ -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 diff --git a/suppliers_portal/patches/oct_12.py b/suppliers_portal/patches/oct_12.py index 9159a87..329b827 100644 --- a/suppliers_portal/patches/oct_12.py +++ b/suppliers_portal/patches/oct_12.py @@ -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() diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/__init__.py b/suppliers_portal/suppliers_portal/doctype/supplier/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.js b/suppliers_portal/suppliers_portal/doctype/supplier/supplier.js deleted file mode 100644 index be91dd9..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.js +++ /dev/null @@ -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) { - -// }, -// }); diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.json b/suppliers_portal/suppliers_portal/doctype/supplier/supplier.json deleted file mode 100644 index e57f319..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.json +++ /dev/null @@ -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" -} \ No newline at end of file diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.py b/suppliers_portal/suppliers_portal/doctype/supplier/supplier.py deleted file mode 100644 index 0da8034..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/supplier.py +++ /dev/null @@ -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() diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier.html b/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier.html deleted file mode 100644 index 63803ce..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "templates/web.html" %} - -{% block page_content %} -

{{ title |e }}

-{% endblock %} - - \ No newline at end of file diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier_row.html b/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier_row.html deleted file mode 100644 index 88db3f7..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/templates/portal_supplier_row.html +++ /dev/null @@ -1,4 +0,0 @@ -
- {{ (doc.title or doc.name) |e }} -
- diff --git a/suppliers_portal/suppliers_portal/doctype/supplier/test_supplier.py b/suppliers_portal/suppliers_portal/doctype/supplier/test_supplier.py deleted file mode 100644 index 03d842c..0000000 --- a/suppliers_portal/suppliers_portal/doctype/supplier/test_supplier.py +++ /dev/null @@ -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 -- 2.50.1 From faf0df9879f67cff3a6a9fd4e41c23912b6a0935 Mon Sep 17 00:00:00 2001 From: Rohan Bansal Date: Fri, 25 Apr 2025 16:08:07 +0530 Subject: [PATCH 2/8] ci: update pre-commit and apply fixes --- .pre-commit-config.yaml | 65 ++- .prettierignore | 222 +++++++++ .prettierrc.cjs | 19 + supplier-portal/.prettierrc.json | 4 +- supplier-portal/package.json | 48 +- supplier-portal/postcss.config.js | 8 +- supplier-portal/src/App.vue | 6 +- supplier-portal/src/assets/Inter/inter.css | 234 +++++----- supplier-portal/src/data/session.js | 150 +++--- supplier-portal/src/data/user.js | 16 +- supplier-portal/src/data/utils.js | 38 +- supplier-portal/src/main.js | 13 +- supplier-portal/src/pages/Login.vue | 24 +- supplier-portal/src/pages/NewInvoice.vue | 199 ++------ supplier-portal/src/pages/SupplierInvoice.vue | 126 ++--- .../src/pages/SupplierInvoiceList.vue | 8 +- supplier-portal/src/router.js | 74 +-- supplier-portal/tailwind.config.js | 30 +- supplier-portal/vite.config.js | 30 +- .../public/supplier-portal/index.html | 2 +- suppliers_portal/suppliers_portal/api.py | 30 +- .../invoice_payment_references.json | 87 ++-- .../doctype/invoices/invoices.json | 430 +++++++++--------- .../doctype/invoices/invoices.py | 140 +++--- .../doctype/invoices/invoices_list.js | 8 +- .../supplier_invoice_items.json | 104 ++--- .../supplier_invoices/supplier_invoices.json | 129 +++--- .../supplier_invoices/supplier_invoices.js | 60 +-- .../supplier_invoices/supplier_invoices.json | 58 +-- .../supplier_invoices/supplier_invoices.py | 62 +-- .../supplier_portal/supplier_portal.json | 124 ++--- .../suppliers_portal/suppliers_portal.json | 94 ++-- suppliers_portal/utils.py | 22 +- suppliers_portal/www/supplier-portal.html | 2 +- 34 files changed, 1347 insertions(+), 1319 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc.cjs diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1a38b03..8f02923 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,14 @@ exclude: 'node_modules|.git' -default_stages: [commit] +default_stages: [pre-commit] fail_fast: false - repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - files: "suppliers_portal.*" - exclude: ".*json$|.*txt$|.*csv|.*md|.*svg" + files: 'suppliers_portal.*' + exclude: '.*json$|.*txt$|.*csv|.*md|.*svg' - id: check-yaml - id: check-merge-conflict - id: check-ast @@ -19,49 +18,41 @@ repos: - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.2.0 + rev: v0.11.7 hooks: - id: ruff - name: "Run ruff linter and apply fixes" - args: ["--fix"] + name: 'Run ruff linter and apply fixes' + args: ['--fix'] - id: ruff-format - name: "Format Python code" - - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 - hooks: - - id: prettier - types_or: [javascript, vue, scss] - # Ignore any files that might contain jinja / bundles - exclude: | - (?x)^( - suppliers_portal/public/dist/.*| - .*node_modules.*| - .*boilerplate.*| - suppliers_portal/templates/includes/.*| - suppliers_portal/public/js/lib/.* - )$ - + name: 'Format Python code' - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.44.0 + rev: v8.56.0 hooks: - id: eslint types_or: [javascript] args: ['--quiet'] # Ignore any files that might contain jinja / bundles exclude: | - (?x)^( - suppliers_portal/public/dist/.*| - cypress/.*| - .*node_modules.*| - .*boilerplate.*| - suppliers_portal/templates/includes/.*| - suppliers_portal/public/js/lib/.* - )$ + (?x)^( + .*boilerplate.*| + .*node_modules.*| + cypress/.*| + suppliers_portal/public/dist/.*| + suppliers_portal/public/js/lib/.*| + suppliers_portal/public/supplier-portal/assets/.*| + suppliers_portal/templates/includes/.* + )$ + + - repo: local + hooks: + - id: prettier + name: prettier + entry: npx prettier -w . --config .prettierrc.cjs --ignore-path .prettierignore + language: system ci: - autoupdate_schedule: weekly - skip: [] - submodules: false + autoupdate_schedule: weekly + skip: [] + submodules: false diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..929a012 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,222 @@ +#------------------------------------------------------------------------------------------------------------------- +# Keep this section in sync with .gitignore +#------------------------------------------------------------------------------------------------------------------- + +*.pyc +*.py~ +*.comp.js +*.DS_Store +locale +.wnf-lang-status +*.swp +*.egg-info +dist/ +# build/ +suppliers_portal/docs/current +suppliers_portal/public/dist +suppliers_portal/public/supplier-portal/assets +.vscode +.vs +node_modules +.kdev4/ +*.kdev4 +*debug.log + +# Not Recommended, but will remove once webpack ready +package-lock.json + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +# build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ +.cypress-coverage + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +.static_storage/ +.media/ +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# cypress +cypress/screenshots +cypress/videos + +# JetBrains IDEs +.idea/ + +#------------------------------------------------------------------------------------------------------------------- +# Prettier-specific overrides +#------------------------------------------------------------------------------------------------------------------- + + +# Package manager files +pnpm-lock.yaml +yarn.lock +package-lock.json +shrinkwrap.json + +# Build outputs +lib +.github + +# Prettier reformats code blocks inside Markdown, which affects rendered output +*.md + +# In a Frappe context HTML files are either Jinja or Resig's microtemplate format and should not be checked +*.html diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..eeb2d4e --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1,19 @@ +module.exports = { + arrowParens: 'avoid', + bracketSameLine: true, + bracketSpacing: true, + embeddedLanguageFormatting: 'auto', + htmlWhitespaceSensitivity: 'css', + insertPragma: false, + jsxSingleQuote: false, + printWidth: 120, + proseWrap: 'preserve', + quoteProps: 'as-needed', + requirePragma: false, + semi: false, + singleQuote: true, + tabWidth: 2, + trailingComma: 'es5', + useTabs: true, + vueIndentScriptAndStyle: false, +} diff --git a/supplier-portal/.prettierrc.json b/supplier-portal/.prettierrc.json index b2095be..1ccc975 100644 --- a/supplier-portal/.prettierrc.json +++ b/supplier-portal/.prettierrc.json @@ -1,4 +1,4 @@ { - "semi": false, - "singleQuote": true + "semi": false, + "singleQuote": true } diff --git a/supplier-portal/package.json b/supplier-portal/package.json index 37960d0..55c5d73 100644 --- a/supplier-portal/package.json +++ b/supplier-portal/package.json @@ -1,26 +1,26 @@ { - "name": "frappe-ui-frontend", - "private": true, - "version": "0.0.0", - "scripts": { - "dev": "vite", - "build": "vite build --base=/assets/suppliers_portal/supplier-portal/ && yarn copy-html-entry", - "preview": "vite preview", - "copy-html-entry": "cp ../suppliers_portal/public/supplier-portal/index.html ../suppliers_portal/www/supplier-portal.html" - }, - "dependencies": { - "bootstrap": "5.2.3", - "bootstrap-icons": "^1.11.3", - "feather-icons": "^4.28.0", - "frappe-ui": "^0.1.69", - "vue": "^3.2.25", - "vue-router": "^4.0.12" - }, - "devDependencies": { - "@vitejs/plugin-vue": "^2.0.0", - "autoprefixer": "^10.4.2", - "postcss": "^8.4.5", - "tailwindcss": "^3.0.15", - "vite": "^2.7.2" - } + "name": "frappe-ui-frontend", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "vite build --base=/assets/suppliers_portal/supplier-portal/ && yarn copy-html-entry", + "preview": "vite preview", + "copy-html-entry": "cp ../suppliers_portal/public/supplier-portal/index.html ../suppliers_portal/www/supplier-portal.html" + }, + "dependencies": { + "bootstrap": "5.2.3", + "bootstrap-icons": "^1.11.3", + "feather-icons": "^4.28.0", + "frappe-ui": "^0.1.69", + "vue": "^3.2.25", + "vue-router": "^4.0.12" + }, + "devDependencies": { + "@vitejs/plugin-vue": "^2.0.0", + "autoprefixer": "^10.4.2", + "postcss": "^8.4.5", + "tailwindcss": "^3.0.15", + "vite": "^2.7.2" + } } diff --git a/supplier-portal/postcss.config.js b/supplier-portal/postcss.config.js index 33ad091..1b69d43 100644 --- a/supplier-portal/postcss.config.js +++ b/supplier-portal/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, } diff --git a/supplier-portal/src/App.vue b/supplier-portal/src/App.vue index a14d0c3..eb1533e 100644 --- a/supplier-portal/src/App.vue +++ b/supplier-portal/src/App.vue @@ -1,5 +1,5 @@ diff --git a/supplier-portal/src/assets/Inter/inter.css b/supplier-portal/src/assets/Inter/inter.css index 3ca1bbf..8fca606 100644 --- a/supplier-portal/src/assets/Inter/inter.css +++ b/supplier-portal/src/assets/Inter/inter.css @@ -1,152 +1,170 @@ @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 100; - font-display: swap; - src: url("Inter-Thin.woff2?v=3.12") format("woff2"), - url("Inter-Thin.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 100; + font-display: swap; + src: + url('Inter-Thin.woff2?v=3.12') format('woff2'), + url('Inter-Thin.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 100; - font-display: swap; - src: url("Inter-ThinItalic.woff2?v=3.12") format("woff2"), - url("Inter-ThinItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 100; + font-display: swap; + src: + url('Inter-ThinItalic.woff2?v=3.12') format('woff2'), + url('Inter-ThinItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 200; - font-display: swap; - src: url("Inter-ExtraLight.woff2?v=3.12") format("woff2"), - url("Inter-ExtraLight.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 200; + font-display: swap; + src: + url('Inter-ExtraLight.woff2?v=3.12') format('woff2'), + url('Inter-ExtraLight.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 200; - font-display: swap; - src: url("Inter-ExtraLightItalic.woff2?v=3.12") format("woff2"), - url("Inter-ExtraLightItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 200; + font-display: swap; + src: + url('Inter-ExtraLightItalic.woff2?v=3.12') format('woff2'), + url('Inter-ExtraLightItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 300; - font-display: swap; - src: url("Inter-Light.woff2?v=3.12") format("woff2"), - url("Inter-Light.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 300; + font-display: swap; + src: + url('Inter-Light.woff2?v=3.12') format('woff2'), + url('Inter-Light.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 300; - font-display: swap; - src: url("Inter-LightItalic.woff2?v=3.12") format("woff2"), - url("Inter-LightItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 300; + font-display: swap; + src: + url('Inter-LightItalic.woff2?v=3.12') format('woff2'), + url('Inter-LightItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url("Inter-Regular.woff2?v=3.12") format("woff2"), - url("Inter-Regular.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 400; + font-display: swap; + src: + url('Inter-Regular.woff2?v=3.12') format('woff2'), + url('Inter-Regular.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 400; - font-display: swap; - src: url("Inter-Italic.woff2?v=3.12") format("woff2"), - url("Inter-Italic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 400; + font-display: swap; + src: + url('Inter-Italic.woff2?v=3.12') format('woff2'), + url('Inter-Italic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 500; - font-display: swap; - src: url("Inter-Medium.woff2?v=3.12") format("woff2"), - url("Inter-Medium.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 500; + font-display: swap; + src: + url('Inter-Medium.woff2?v=3.12') format('woff2'), + url('Inter-Medium.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 500; - font-display: swap; - src: url("Inter-MediumItalic.woff2?v=3.12") format("woff2"), - url("Inter-MediumItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 500; + font-display: swap; + src: + url('Inter-MediumItalic.woff2?v=3.12') format('woff2'), + url('Inter-MediumItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url("Inter-SemiBold.woff2?v=3.12") format("woff2"), - url("Inter-SemiBold.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 600; + font-display: swap; + src: + url('Inter-SemiBold.woff2?v=3.12') format('woff2'), + url('Inter-SemiBold.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 600; - font-display: swap; - src: url("Inter-SemiBoldItalic.woff2?v=3.12") format("woff2"), - url("Inter-SemiBoldItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 600; + font-display: swap; + src: + url('Inter-SemiBoldItalic.woff2?v=3.12') format('woff2'), + url('Inter-SemiBoldItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 700; - font-display: swap; - src: url("Inter-Bold.woff2?v=3.12") format("woff2"), - url("Inter-Bold.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: + url('Inter-Bold.woff2?v=3.12') format('woff2'), + url('Inter-Bold.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 700; - font-display: swap; - src: url("Inter-BoldItalic.woff2?v=3.12") format("woff2"), - url("Inter-BoldItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 700; + font-display: swap; + src: + url('Inter-BoldItalic.woff2?v=3.12') format('woff2'), + url('Inter-BoldItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 800; - font-display: swap; - src: url("Inter-ExtraBold.woff2?v=3.12") format("woff2"), - url("Inter-ExtraBold.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 800; + font-display: swap; + src: + url('Inter-ExtraBold.woff2?v=3.12') format('woff2'), + url('Inter-ExtraBold.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 800; - font-display: swap; - src: url("Inter-ExtraBoldItalic.woff2?v=3.12") format("woff2"), - url("Inter-ExtraBoldItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 800; + font-display: swap; + src: + url('Inter-ExtraBoldItalic.woff2?v=3.12') format('woff2'), + url('Inter-ExtraBoldItalic.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: normal; - font-weight: 900; - font-display: swap; - src: url("Inter-Black.woff2?v=3.12") format("woff2"), - url("Inter-Black.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: normal; + font-weight: 900; + font-display: swap; + src: + url('Inter-Black.woff2?v=3.12') format('woff2'), + url('Inter-Black.woff?v=3.12') format('woff'); } @font-face { - font-family: 'Inter'; - font-style: italic; - font-weight: 900; - font-display: swap; - src: url("Inter-BlackItalic.woff2?v=3.12") format("woff2"), - url("Inter-BlackItalic.woff?v=3.12") format("woff"); + font-family: 'Inter'; + font-style: italic; + font-weight: 900; + font-display: swap; + src: + url('Inter-BlackItalic.woff2?v=3.12') format('woff2'), + url('Inter-BlackItalic.woff?v=3.12') format('woff'); } diff --git a/supplier-portal/src/data/session.js b/supplier-portal/src/data/session.js index 46f3e59..8c213b7 100644 --- a/supplier-portal/src/data/session.js +++ b/supplier-portal/src/data/session.js @@ -5,87 +5,87 @@ import { createResource } from 'frappe-ui' import { userResource } from './user' export function sessionUser() { - const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) - let _sessionUser = cookies.get('user_id') - if (_sessionUser === 'Guest') { - _sessionUser = null - } - return _sessionUser + const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) + let _sessionUser = cookies.get('user_id') + if (_sessionUser === 'Guest') { + _sessionUser = null + } + return _sessionUser } export function sessionSupplierId() { - const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) - return cookies.get('supplier_id') + const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) + return cookies.get('supplier_id') } export function sessionSupplierName() { - const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) - return cookies.get('supplier_name') + const cookies = new URLSearchParams(document.cookie.split('; ').join('&')) + return cookies.get('supplier_name') } export const session = reactive({ - supplier_login: createResource({ - url: 'suppliers_portal.suppliers_portal.api.validate_supplier_id', - makeParams({ supplier_id }) { - return { - supplier_id, - } - }, - onSuccess(data) { - if (data && data.status === 'success') { - userResource.reload() - session.user = sessionUser() - document.cookie = `supplier_id=${data.supplier_id}` - document.cookie = `supplier_name=${data.supplier_name}` - session.supplier_id = data.supplier_id - session.supplier_name = data.supplier_name - session.login.reset() - router.replace({ name: 'SupplierInvoiceList' }) - } - }, - onError(error) { - console.error('Login failed', error) - }, - }), - login: createResource({ - url: 'login', - makeParams({ email, password }) { - return { - usr: email, - pwd: password, - } - }, - onSuccess(data) { - if (data) { - userResource.reload() - session.user = sessionUser() - // clear supplier_id and supplier_name - document.cookie = `supplier_id=${session.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`; - document.cookie = `supplier_id=${session.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`; - session.supplier_id = '' - session.supplier_name = '' - session.login.reset() - router.replace({ name: 'SupplierInvoiceList' }) - } - }, - onError(error) { - console.error('Login failed', error) - }, - }), - logout: createResource({ - url: 'logout', - onSuccess() { - document.cookie = `supplier_id=${session.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`; - document.cookie = `supplier_id=${session.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`; - session.supplier_id = '' - session.supplier_name = '' - userResource.reset() - session.user = sessionUser() - router.replace({ name: 'Login' }) - }, - }), - user: sessionUser(), - supplier_id: sessionSupplierId(), - supplier_name: sessionSupplierName(), - isLoggedIn: computed(() => !!session.user), -}) \ No newline at end of file + supplier_login: createResource({ + url: 'suppliers_portal.suppliers_portal.api.validate_supplier_id', + makeParams({ supplier_id }) { + return { + supplier_id, + } + }, + onSuccess(data) { + if (data && data.status === 'success') { + userResource.reload() + session.user = sessionUser() + document.cookie = `supplier_id=${data.supplier_id}` + document.cookie = `supplier_name=${data.supplier_name}` + session.supplier_id = data.supplier_id + session.supplier_name = data.supplier_name + session.login.reset() + router.replace({ name: 'SupplierInvoiceList' }) + } + }, + onError(error) { + console.error('Login failed', error) + }, + }), + login: createResource({ + url: 'login', + makeParams({ email, password }) { + return { + usr: email, + pwd: password, + } + }, + onSuccess(data) { + if (data) { + userResource.reload() + session.user = sessionUser() + // clear supplier_id and supplier_name + document.cookie = `supplier_id=${session.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'` + document.cookie = `supplier_id=${session.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'` + session.supplier_id = '' + session.supplier_name = '' + session.login.reset() + router.replace({ name: 'SupplierInvoiceList' }) + } + }, + onError(error) { + console.error('Login failed', error) + }, + }), + logout: createResource({ + url: 'logout', + onSuccess() { + document.cookie = `supplier_id=${session.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'` + document.cookie = `supplier_id=${session.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'` + session.supplier_id = '' + session.supplier_name = '' + userResource.reset() + session.user = sessionUser() + router.replace({ name: 'Login' }) + }, + }), + user: sessionUser(), + supplier_id: sessionSupplierId(), + supplier_name: sessionSupplierName(), + isLoggedIn: computed(() => !!session.user), +}) diff --git a/supplier-portal/src/data/user.js b/supplier-portal/src/data/user.js index 876b359..95766d5 100644 --- a/supplier-portal/src/data/user.js +++ b/supplier-portal/src/data/user.js @@ -2,11 +2,11 @@ import router from '@/router' import { createResource } from 'frappe-ui' export const userResource = createResource({ - url: 'frappe.auth.get_logged_user', - cache: 'User', - onError(error) { - if (error && error.exc_type === 'AuthenticationError') { - router.push({ name: 'LoginPage' }) - } - }, -}) \ No newline at end of file + url: 'frappe.auth.get_logged_user', + cache: 'User', + onError(error) { + if (error && error.exc_type === 'AuthenticationError') { + router.push({ name: 'LoginPage' }) + } + }, +}) diff --git a/supplier-portal/src/data/utils.js b/supplier-portal/src/data/utils.js index 8f4d55c..9adaf5b 100644 --- a/supplier-portal/src/data/utils.js +++ b/supplier-portal/src/data/utils.js @@ -1,22 +1,22 @@ export const addDays = (date, days) => { - const result = new Date(date); - result.setDate(result.getDate() + days); - return result; + const result = new Date(date) + result.setDate(result.getDate() + days) + return result } -export const parseInvoiceTerms = (invoiceTerm) => { - switch (invoiceTerm) { - case 'NET 10': - return 10; - case 'NET 15': - return 15; - case 'NET 30': - return 30; - case 'NET 45': - return 45; - case 'NET 60': - return 60; - default: - return 0; - } -} \ No newline at end of file +export const parseInvoiceTerms = invoiceTerm => { + switch (invoiceTerm) { + case 'NET 10': + return 10 + case 'NET 15': + return 15 + case 'NET 30': + return 30 + case 'NET 45': + return 45 + case 'NET 60': + return 60 + default: + return 0 + } +} diff --git a/supplier-portal/src/main.js b/supplier-portal/src/main.js index 3420fdd..aec5d08 100644 --- a/supplier-portal/src/main.js +++ b/supplier-portal/src/main.js @@ -7,16 +7,7 @@ import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap' import 'bootstrap-icons/font/bootstrap-icons.css' - - -import { - Button, - Card, - Input, - setConfig, - frappeRequest, - resourcesPlugin, -} from 'frappe-ui' +import { Button, Card, Input, setConfig, frappeRequest, resourcesPlugin } from 'frappe-ui' let app = createApp(App) @@ -29,4 +20,4 @@ app.component('Button', Button) app.component('Card', Card) app.component('Input', Input) -app.mount('#app') \ No newline at end of file +app.mount('#app') diff --git a/supplier-portal/src/pages/Login.vue b/supplier-portal/src/pages/Login.vue index 950fe8a..8a0d079 100644 --- a/supplier-portal/src/pages/Login.vue +++ b/supplier-portal/src/pages/Login.vue @@ -4,29 +4,11 @@
- - + +
- +
diff --git a/supplier-portal/src/pages/NewInvoice.vue b/supplier-portal/src/pages/NewInvoice.vue index d71b401..fb0a40c 100644 --- a/supplier-portal/src/pages/NewInvoice.vue +++ b/supplier-portal/src/pages/NewInvoice.vue @@ -12,13 +12,7 @@ - +
@@ -29,24 +23,19 @@
- +
+ v-model.number="invoiceData.supplier_invoice_number" />
- +
+ v-model="invoiceData.service_date" />
@@ -65,25 +53,20 @@
- +
+ v-model="invoiceData.supplier" />
- +
+ v-model="invoiceData.invoice_date" />
@@ -102,9 +84,7 @@
- +
+ v-if="!isAdministrator" /> + v-else />
- +
@@ -153,9 +125,7 @@
- +
+ v-model="invoiceDueDate" />
@@ -182,80 +151,43 @@ data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" - aria-controls="collapseOne" - > + aria-controls="collapseOne"> Service Address
+ data-bs-parent="#service-address-accordion">
- +
- +
- +
- +
- +
- +
- +
- +
- +
- +
@@ -273,21 +205,14 @@ data-bs-toggle="collapse" data-bs-target="#collapse-2" aria-expanded="true" - aria-controls="collapse-2" - > + aria-controls="collapse-2"> Notes -
+
- +