feat: migrate to ERPNext #20
79 changed files with 1345 additions and 1692 deletions
|
|
@ -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
|
||||
|
|
|
|||
222
.prettierignore
Normal file
222
.prettierignore
Normal file
|
|
@ -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
|
||||
19
.prettierrc.cjs
Normal file
19
.prettierrc.cjs
Normal file
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<router-view />
|
||||
</div>
|
||||
<div>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
})
|
||||
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),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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' })
|
||||
}
|
||||
},
|
||||
})
|
||||
url: 'frappe.auth.get_logged_user',
|
||||
cache: 'User',
|
||||
onError(error) {
|
||||
if (error && error.exc_type === 'AuthenticationError') {
|
||||
router.push({ name: 'LoginPage' })
|
||||
}
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
app.mount('#app')
|
||||
|
|
|
|||
|
|
@ -4,29 +4,11 @@
|
|||
<form class="flex flex-col space-y-2 w-full" @submit.prevent="submit">
|
||||
<Switch v-model="state.forAdmin" label="Login as Admin" />
|
||||
<div v-if="state.forAdmin">
|
||||
<Input
|
||||
required
|
||||
name="email"
|
||||
type="text"
|
||||
placeholder="username@email.com"
|
||||
label="User ID"
|
||||
/>
|
||||
<Input
|
||||
required
|
||||
name="password"
|
||||
type="password"
|
||||
placeholder="••••••"
|
||||
label="Password"
|
||||
/>
|
||||
<Input required name="email" type="text" placeholder="username@email.com" label="User ID" />
|
||||
<Input required name="password" type="password" placeholder="••••••" label="Password" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<Input
|
||||
required
|
||||
name="supplier_id"
|
||||
type="text"
|
||||
placeholder="Supplier ID"
|
||||
label="Supplier ID"
|
||||
/>
|
||||
<Input required name="supplier_id" type="text" placeholder="Supplier ID" label="Supplier ID" />
|
||||
</div>
|
||||
<Button :loading="session.login.loading" appearance="primary">Login</Button>
|
||||
<div v-if="session.login.error" class="text-red-500">
|
||||
|
|
|
|||
|
|
@ -12,13 +12,7 @@
|
|||
<button type="button" class="btn btn-primary btn-light">
|
||||
<router-link to="/">Cancel</router-link>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-dark"
|
||||
@click="saveInvoice"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-dark" @click="saveInvoice">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -29,24 +23,19 @@
|
|||
<div class="mb-2 row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="supplier_invoice_number" class="col-sm-3 col-form-label"
|
||||
>Invoice Number</label
|
||||
>
|
||||
<label for="bill_no" class="col-sm-3 col-form-label">Invoice Number</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="supplier_invoice_number"
|
||||
v-model.number="invoiceData.supplier_invoice_number"
|
||||
/>
|
||||
id="bill_no"
|
||||
v-model.number="invoiceData.bill_no" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="service_date" class="col-sm-3 col-form-label"
|
||||
>Service Date</label
|
||||
>
|
||||
<label for="service_date" class="col-sm-3 col-form-label">Service Date</label>
|
||||
<div class="col-sm-8">
|
||||
<TextInput
|
||||
:type="'date'"
|
||||
|
|
@ -55,8 +44,7 @@
|
|||
placeholder="Service Date"
|
||||
id="service_date"
|
||||
:disabled="false"
|
||||
v-model="invoiceData.service_date"
|
||||
/>
|
||||
v-model="invoiceData.service_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -65,35 +53,29 @@
|
|||
<div class="mb-2 row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="supplier_id" class="col-sm-3 col-form-label"
|
||||
>Supplier ID</label
|
||||
>
|
||||
<label for="supplier_id" class="col-sm-3 col-form-label">Supplier ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="supplier_id"
|
||||
v-model="invoiceData.supplier"
|
||||
/>
|
||||
v-model="invoiceData.supplier" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="invoice_date" class="col-sm-3 col-form-label"
|
||||
>Invoice Date</label
|
||||
>
|
||||
<label for="posting_date" class="col-sm-3 col-form-label">Invoice Date</label>
|
||||
<div class="col-sm-8">
|
||||
<TextInput
|
||||
:type="'date'"
|
||||
size="lg"
|
||||
variant="outline"
|
||||
placeholder="Invoice Date"
|
||||
id="invoice_date"
|
||||
id="posting_date"
|
||||
:disabled="false"
|
||||
v-model="invoiceData.invoice_date"
|
||||
/>
|
||||
v-model="invoiceData.posting_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -102,9 +84,7 @@
|
|||
<div class="mb-2 row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="supplier_name" class="col-sm-3 col-form-label"
|
||||
>Supplier Name</label
|
||||
>
|
||||
<label for="supplier_name" class="col-sm-3 col-form-label">Supplier Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
|
|
@ -112,36 +92,28 @@
|
|||
class="form-control-plaintext"
|
||||
id="supplier_name"
|
||||
:value="invoiceData.supplier_name"
|
||||
v-if="!isAdministrator"
|
||||
/>
|
||||
v-if="!isAdministrator" />
|
||||
<Autocomplete
|
||||
id="supplier_name"
|
||||
:options="portalSuppliers.data"
|
||||
:placeholder="'Supplier Name'"
|
||||
:hideSearch="!invoiceData.supplier_invoice_number"
|
||||
:hideSearch="!invoiceData.bill_no"
|
||||
:label="'company_name'"
|
||||
v-model="invoiceData.supplier_name"
|
||||
v-else
|
||||
/>
|
||||
v-else />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="invoice_terms" class="col-sm-3 col-form-label"
|
||||
>Invoice Terms</label
|
||||
>
|
||||
<label for="payment_terms_template" class="col-sm-3 col-form-label">Invoice Terms</label>
|
||||
<div class="col-sm-8">
|
||||
<select
|
||||
class="form-select form-select-mb h-100 d-inline-block"
|
||||
aria-label="Invoice Terms"
|
||||
v-model="invoiceData.invoice_terms"
|
||||
>
|
||||
v-model="invoiceData.payment_terms_template">
|
||||
<option readonly></option>
|
||||
<option
|
||||
v-for="option in invoiceTermOptions"
|
||||
:value="option.value"
|
||||
>
|
||||
<option v-for="option in invoiceTermOptions" :value="option.value">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
|
|
@ -153,9 +125,7 @@
|
|||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="invoice_date" class="col-sm-3 col-form-label"
|
||||
>Due Date</label
|
||||
>
|
||||
<label for="posting_date" class="col-sm-3 col-form-label">Due Date</label>
|
||||
<div class="col-sm-8">
|
||||
<TextInput
|
||||
:type="'date'"
|
||||
|
|
@ -164,8 +134,7 @@
|
|||
placeholder="Due Date"
|
||||
id="due_date"
|
||||
:disabled="true"
|
||||
v-model="invoiceDueDate"
|
||||
/>
|
||||
v-model="invoiceDueDate" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -182,80 +151,43 @@
|
|||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseOne"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapseOne"
|
||||
>
|
||||
aria-controls="collapseOne">
|
||||
Service Address
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
id="collapseOne"
|
||||
class="accordion-collapse collapse show"
|
||||
data-bs-parent="#service-address-accordion"
|
||||
>
|
||||
data-bs-parent="#service-address-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row mb-1">
|
||||
<label for="site_code" class="col-sm-3 col-form-label"
|
||||
>Site Code</label
|
||||
>
|
||||
<label for="site_code" class="col-sm-3 col-form-label">Site Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="site_code"
|
||||
v-model="invoiceData.site_code"
|
||||
/>
|
||||
<input type="text" class="form-control" id="site_code" v-model="invoiceData.site_code" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="street" class="col-sm-3 col-form-label"
|
||||
>Street</label
|
||||
>
|
||||
<label for="street" class="col-sm-3 col-form-label">Street</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="street"
|
||||
v-model="invoiceData.street"
|
||||
/>
|
||||
<input type="text" class="form-control" id="street" v-model="invoiceData.street" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="city" class="col-sm-3 col-form-label"
|
||||
>City</label
|
||||
>
|
||||
<label for="city" class="col-sm-3 col-form-label">City</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="city"
|
||||
v-model="invoiceData.city"
|
||||
/>
|
||||
<input type="text" class="form-control" id="city" v-model="invoiceData.city" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="state" class="col-sm-3 col-form-label"
|
||||
>State</label
|
||||
>
|
||||
<label for="state" class="col-sm-3 col-form-label">State</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="state"
|
||||
v-model="invoiceData.state"
|
||||
/>
|
||||
<input type="text" class="form-control" id="state" v-model="invoiceData.state" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="zip_code" class="col-sm-3 col-form-label"
|
||||
>Zip Code</label
|
||||
>
|
||||
<label for="zip_code" class="col-sm-3 col-form-label">Zip Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="zip_code"
|
||||
v-model="invoiceData.zip_code"
|
||||
/>
|
||||
<input type="text" class="form-control" id="zip_code" v-model="invoiceData.zip_code" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -273,30 +205,22 @@
|
|||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse-2"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapse-2"
|
||||
>
|
||||
aria-controls="collapse-2">
|
||||
Notes
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
id="collapse-2"
|
||||
class="accordion-collapse collapse show"
|
||||
data-bs-parent="#address-accordion"
|
||||
>
|
||||
<div id="collapse-2" class="accordion-collapse collapse show" data-bs-parent="#address-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row mb-1">
|
||||
<label for="notes" class="col-sm-3 col-form-label"
|
||||
>Notes</label
|
||||
>
|
||||
<label for="remarks" class="col-sm-3 col-form-label">Notes</label>
|
||||
<div class="col-md-8">
|
||||
<Textarea
|
||||
:variant="'subtle'"
|
||||
size="md"
|
||||
placeholder="Enter Notes Here"
|
||||
:disabled="false"
|
||||
v-model="invoiceData.notes"
|
||||
id="notes"
|
||||
/>
|
||||
v-model="invoiceData.remarks"
|
||||
id="remarks" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -327,44 +251,27 @@
|
|||
<select
|
||||
class="form-select form-select-mb h-100 d-inline-block"
|
||||
aria-label="Service Types"
|
||||
v-model="item.service_type"
|
||||
>
|
||||
v-model="item.item_code">
|
||||
<option readonly></option>
|
||||
<option
|
||||
v-for="option in serviceTypeOption"
|
||||
:value="option.value"
|
||||
>
|
||||
<option v-for="option in serviceTypeOption" :value="option.value">
|
||||
{{ option.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control" v-model="item.quantity" />
|
||||
<input type="text" class="form-control" v-model="item.qty" />
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"
|
||||
><i class="bi bi-currency-dollar"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
v-model.number="item.rate"
|
||||
/>
|
||||
<span class="input-group-text"><i class="bi bi-currency-dollar"></i> </span>
|
||||
<input type="text" class="form-control" v-model.number="item.rate" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"
|
||||
><i class="bi bi-currency-dollar"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control"
|
||||
v-model.number="item.amount"
|
||||
/>
|
||||
<span class="input-group-text"><i class="bi bi-currency-dollar"></i> </span>
|
||||
<input type="text" readonly class="form-control" v-model.number="item.amount" />
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
|
|
@ -390,20 +297,11 @@
|
|||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<label for="total_amount" class="col-sm-3 col-form-label"
|
||||
>Total Amount</label
|
||||
>
|
||||
<label for="grand_total" class="col-sm-3 col-form-label">Total Amount</label>
|
||||
<div class="cols-sm-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text currency-fmt"
|
||||
><i class="bi bi-currency-dollar"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control"
|
||||
v-model.number="invoiceData.total_amount"
|
||||
/>
|
||||
<span class="input-group-text currency-fmt"><i class="bi bi-currency-dollar"></i> </span>
|
||||
<input type="text" readonly class="form-control" v-model.number="invoiceData.grand_total" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -427,7 +325,7 @@ import Autocomplete from 'frappe-ui/src/components/Autocomplete.vue'
|
|||
const isAdministrator = sessionUser() === 'Administrator'
|
||||
|
||||
const portalInvoice = createListResource({
|
||||
doctype: 'Invoices',
|
||||
doctype: 'Purchase Invoice',
|
||||
insert: {
|
||||
onSuccess() {
|
||||
router.push({ name: 'SupplierInvoiceList' })
|
||||
|
|
@ -435,6 +333,13 @@ const portalInvoice = createListResource({
|
|||
},
|
||||
})
|
||||
|
||||
const portalSuppliers = createListResource({
|
||||
doctype: 'Supplier',
|
||||
fields: ['name as value', 'supplier_name as label'],
|
||||
orderBy: 'creation desc',
|
||||
})
|
||||
portalSuppliers.fetch()
|
||||
|
||||
const invoiceTermOptions = [
|
||||
{
|
||||
label: 'NET 10',
|
||||
|
|
@ -474,28 +379,25 @@ const serviceTypeOption = [
|
|||
]
|
||||
|
||||
const invoiceData = reactive({
|
||||
supplier_invoice_number: '',
|
||||
bill_no: '',
|
||||
supplier: sessionSupplierId(),
|
||||
service_date: new Date().toISOString().split('T')[0],
|
||||
invoice_date: new Date().toISOString().split('T')[0],
|
||||
posting_date: new Date().toISOString().split('T')[0],
|
||||
supplier_name: sessionSupplierName(),
|
||||
invoice_terms: '',
|
||||
total_amount: 0,
|
||||
payment_terms_template: '',
|
||||
grand_total: 0,
|
||||
site_code: '',
|
||||
street: '',
|
||||
city: '',
|
||||
state: '',
|
||||
zip_code: '',
|
||||
notes: '',
|
||||
remarks: '',
|
||||
})
|
||||
|
||||
const invoiceItems = reactive([])
|
||||
let invoiceDueDate = computed(() => {
|
||||
if (invoiceData.invoice_terms) {
|
||||
let result = addDays(
|
||||
invoiceData.invoice_date,
|
||||
parseInvoiceTerms(invoiceData.invoice_terms),
|
||||
)
|
||||
if (invoiceData.payment_terms_template) {
|
||||
let result = addDays(invoiceData.posting_date, parseInvoiceTerms(invoiceData.payment_terms_template))
|
||||
// return as string
|
||||
return result.toISOString().split('T')[0]
|
||||
}
|
||||
|
|
@ -504,61 +406,52 @@ let invoiceDueDate = computed(() => {
|
|||
|
||||
const addRow = () => {
|
||||
const rowObject = reactive({
|
||||
service_type: '',
|
||||
quantity: 0,
|
||||
item_code: '',
|
||||
qty: 0,
|
||||
rate: 0,
|
||||
amount: 0,
|
||||
})
|
||||
rowObject.amount = computed(() => rowObject.quantity * rowObject.rate)
|
||||
rowObject.amount = computed(() => rowObject.qty * rowObject.rate)
|
||||
invoiceItems.push(rowObject)
|
||||
}
|
||||
|
||||
const deleteRow = (index) => {
|
||||
const deleteRow = index => {
|
||||
invoiceItems.splice(index, 1)
|
||||
}
|
||||
|
||||
invoiceData.total_amount = computed(() => {
|
||||
invoiceData.grand_total = computed(() => {
|
||||
return invoiceItems.reduce((acc, item) => acc + item.amount, 0)
|
||||
})
|
||||
|
||||
const saveInvoice = () => {
|
||||
portalInvoice.insert.submit({
|
||||
supplier_invoice_number: invoiceData.supplier_invoice_number,
|
||||
supplier: invoiceData.supplier,
|
||||
service_date: invoiceData.service_date,
|
||||
invoice_date: invoiceData.invoice_date,
|
||||
supplier_name: invoiceData.supplier_name,
|
||||
invoice_terms: invoiceData.invoice_terms,
|
||||
...invoiceData,
|
||||
set_posting_time: true,
|
||||
due_date: invoiceDueDate.value,
|
||||
site_code: invoiceData.site_code,
|
||||
street: invoiceData.street,
|
||||
city: invoiceData.city,
|
||||
state: invoiceData.state,
|
||||
zip_code: invoiceData.zip_code,
|
||||
notes: invoiceData.notes,
|
||||
supplier_invoice_items: invoiceItems.map((item) => {
|
||||
remarks: invoiceData.remarks,
|
||||
items: invoiceItems.map(item => {
|
||||
return {
|
||||
service_type: item.service_type,
|
||||
quantity: item.quantity,
|
||||
item_code: item.item_code,
|
||||
qty: item.qty,
|
||||
rate: item.rate,
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
const portalSuppliers = createListResource({
|
||||
doctype: 'Supplier',
|
||||
fields: ['name as value', 'company_name as label'],
|
||||
orderBy: 'creation desc',
|
||||
})
|
||||
portalSuppliers.fetch()
|
||||
|
||||
// watch for changes in invoiceData.supplier_name
|
||||
watch(
|
||||
() => invoiceData.supplier_name,
|
||||
(newVal) => {
|
||||
newVal => {
|
||||
if (newVal) {
|
||||
invoiceData.supplier = newVal.value
|
||||
}
|
||||
},
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div v-if="!invoice.loading && invoice.doc">
|
||||
<div v-if="!invoice.loading && documents.invoice">
|
||||
<div class="container mt-5 py-3 border">
|
||||
<div class="row mb-3 vr-aligned">
|
||||
<div class="col-6">
|
||||
<h1 class="text-gray-900 font-bold text-[32px]">
|
||||
Supplier Invoice: {{ invoice.doc.name }}
|
||||
Supplier Invoice: {{ documents.invoice.name }}
|
||||
<Badge>
|
||||
{{ invoice.doc.status }}
|
||||
{{ documents.invoice.status }}
|
||||
</Badge>
|
||||
</h1>
|
||||
</div>
|
||||
|
|
@ -22,35 +22,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
:class="
|
||||
invoice.doc.supplier_invoice_items
|
||||
? 'container border-bottom pb-3'
|
||||
: 'container pb-3'
|
||||
"
|
||||
>
|
||||
<div :class="documents.invoice.items ? 'container border-bottom pb-3' : 'container pb-3'">
|
||||
<div class="mb-1 row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="suppplier_id" class="col-sm-3 col-form-label"
|
||||
>Supplier ID</label
|
||||
>
|
||||
<label for="suppplier_id" class="col-sm-3 col-form-label">Supplier ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="suppplier_id"
|
||||
:value="invoice.doc.supplier"
|
||||
/>
|
||||
:value="documents.invoice.supplier" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row mb-1">
|
||||
<label for="service_date" class="col-sm-3 col-form-label"
|
||||
>Service Date</label
|
||||
>
|
||||
<label for="service_date" class="col-sm-3 col-form-label">Service Date</label>
|
||||
<div class="col-sm-8">
|
||||
<TextInput
|
||||
:type="'date'"
|
||||
|
|
@ -59,8 +48,7 @@
|
|||
placeholder="Payment Date"
|
||||
id="service_date"
|
||||
:disabled="true"
|
||||
v-model="invoice.doc.service_date"
|
||||
/>
|
||||
v-model="documents.invoice.service_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -68,35 +56,29 @@
|
|||
<div class="mb-1 row">
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="suppplier_name" class="col-sm-3 col-form-label"
|
||||
>Supplier Name</label
|
||||
>
|
||||
<label for="suppplier_name" class="col-sm-3 col-form-label">Supplier Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="suppplier_name"
|
||||
:value="invoice.doc.supplier_name"
|
||||
/>
|
||||
:value="documents.invoice.supplier_name" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row mb-1">
|
||||
<label for="invoice_date" class="col-sm-3 col-form-label"
|
||||
>Invoice Date</label
|
||||
>
|
||||
<label for="posting_date" class="col-sm-3 col-form-label">Invoice Date</label>
|
||||
<div class="col-sm-8">
|
||||
<TextInput
|
||||
:type="'date'"
|
||||
size="lg"
|
||||
variant="outline"
|
||||
placeholder="Invoice Date"
|
||||
id="invoice_date"
|
||||
id="posting_date"
|
||||
:disabled="true"
|
||||
v-model="invoice.doc.invoice_date"
|
||||
/>
|
||||
v-model="documents.invoice.posting_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -114,8 +96,7 @@
|
|||
placeholder="Due Date"
|
||||
id="due_date"
|
||||
:disabled="true"
|
||||
v-model="invoice.doc.due_date"
|
||||
/>
|
||||
v-model="documents.invoice.due_date" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -124,17 +105,14 @@
|
|||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="row">
|
||||
<label for="invoice_terms" class="col-sm-3 col-form-label"
|
||||
>Invoice Terms</label
|
||||
>
|
||||
<label for="payment_terms_template" class="col-sm-3 col-form-label">Invoice Terms</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext h-100 d-inline-block"
|
||||
id="invoice_terms"
|
||||
:value="invoice.doc.invoice_terms"
|
||||
/>
|
||||
id="payment_terms_template"
|
||||
:value="documents.invoice.payment_terms_template" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -150,85 +128,65 @@
|
|||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapseOne"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapseOne"
|
||||
>
|
||||
aria-controls="collapseOne">
|
||||
Service Address
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
id="collapseOne"
|
||||
class="accordion-collapse collapse"
|
||||
data-bs-parent="#service-address-accordion"
|
||||
>
|
||||
<div id="collapseOne" class="accordion-collapse collapse" data-bs-parent="#service-address-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row mb-1">
|
||||
<label for="site_code" class="col-sm-3 col-form-label"
|
||||
>Site Code</label
|
||||
>
|
||||
<label for="site_code" class="col-sm-3 col-form-label">Site Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="site_code"
|
||||
:value="invoice.doc.site_code"
|
||||
/>
|
||||
:value="documents?.address?.doc?.site_code" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="street" class="col-sm-3 col-form-label"
|
||||
>Street</label
|
||||
>
|
||||
<label for="street" class="col-sm-3 col-form-label">Street</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="street"
|
||||
:value="invoice.doc.street"
|
||||
/>
|
||||
:value="documents?.address?.doc?.address_line1" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="city" class="col-sm-3 col-form-label"
|
||||
>City</label
|
||||
>
|
||||
<label for="city" class="col-sm-3 col-form-label">City</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="city"
|
||||
:value="invoice.doc.city"
|
||||
/>
|
||||
:value="documents?.address?.doc?.city" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="state" class="col-sm-3 col-form-label"
|
||||
>State</label
|
||||
>
|
||||
<label for="state" class="col-sm-3 col-form-label">State</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="state"
|
||||
:value="invoice.doc.state"
|
||||
/>
|
||||
:value="documents?.address?.doc?.state" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1">
|
||||
<label for="zip_code" class="col-sm-3 col-form-label"
|
||||
>Zip Code</label
|
||||
>
|
||||
<label for="zip_code" class="col-sm-3 col-form-label">Zip Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control-plaintext"
|
||||
id="zip_code"
|
||||
:value="invoice.doc.zip_code"
|
||||
/>
|
||||
:value="documents?.address?.doc?.pincode" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -246,31 +204,23 @@
|
|||
data-bs-toggle="collapse"
|
||||
data-bs-target="#collapse-2"
|
||||
aria-expanded="true"
|
||||
aria-controls="collapse-2"
|
||||
>
|
||||
aria-controls="collapse-2">
|
||||
Notes
|
||||
</button>
|
||||
</h2>
|
||||
<div
|
||||
id="collapse-2"
|
||||
class="accordion-collapse collapse"
|
||||
data-bs-parent="#address-accordion"
|
||||
>
|
||||
<div id="collapse-2" class="accordion-collapse collapse" data-bs-parent="#address-accordion">
|
||||
<div class="accordion-body">
|
||||
<div class="row mb-1">
|
||||
<label for="notes" class="col-sm-4 col-form-label"
|
||||
>Notes</label
|
||||
>
|
||||
<label for="remarks" class="col-sm-4 col-form-label">Notes</label>
|
||||
<div class="col-sm-8">
|
||||
<Textarea
|
||||
:variant="'subtle'"
|
||||
size="md"
|
||||
placeholder="Enter Notes Here"
|
||||
:disabled="true"
|
||||
v-model="invoice.doc.notes"
|
||||
v-model="documents.invoice.remarks"
|
||||
label=""
|
||||
id="notes"
|
||||
/>
|
||||
id="remarks" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -281,7 +231,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container" v-if="invoice.doc.supplier_invoice_items">
|
||||
<div class="container" v-if="documents.invoice.items">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -293,13 +243,10 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody class="table-group-divider">
|
||||
<tr
|
||||
v-for="(item, index) in invoice.doc.supplier_invoice_items"
|
||||
:key="item.name"
|
||||
>
|
||||
<tr v-for="(item, index) in documents.invoice.items" :key="item.name">
|
||||
<th scope="row">{{ index + 1 }}</th>
|
||||
<td>{{ item.service_type }}</td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
<td>{{ item.item_code }}</td>
|
||||
<td>{{ item.qty }}</td>
|
||||
<td><i class="bi bi-currency-dollar"></i> {{ item.rate }}</td>
|
||||
<td><i class="bi bi-currency-dollar"></i> {{ item.amount }}</td>
|
||||
</tr>
|
||||
|
|
@ -312,20 +259,11 @@
|
|||
<div class="col"></div>
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<label for="total_amount" class="col-sm-3 col-form-label"
|
||||
>Total Amount</label
|
||||
>
|
||||
<label for="grand_total" class="col-sm-3 col-form-label">Total Amount</label>
|
||||
<div class="cols-sm-5">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"
|
||||
><i class="bi bi-currency-dollar"></i>
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
class="form-control"
|
||||
v-model.number="invoice.doc.total_amount"
|
||||
/>
|
||||
<span class="input-group-text"><i class="bi bi-currency-dollar"></i> </span>
|
||||
<input type="text" readonly class="form-control" v-model.number="documents.invoice.grand_total" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -336,22 +274,28 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { createDocumentResource, createListResource, Badge } from 'frappe-ui'
|
||||
import Textarea from 'frappe-ui/src/components/Textarea.vue'
|
||||
import TextInput from 'frappe-ui/src/components/TextInput.vue'
|
||||
import { reactive, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
supplierInvoiceNumber: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const { supplierInvoiceNumber } = defineProps<{
|
||||
supplierInvoiceNumber: string
|
||||
}>()
|
||||
|
||||
const documents = reactive({})
|
||||
const invoice = createDocumentResource({
|
||||
doctype: 'Invoices',
|
||||
name: props.supplierInvoiceNumber,
|
||||
auto: true,
|
||||
doctype: 'Purchase Invoice',
|
||||
name: supplierInvoiceNumber,
|
||||
onSuccess: (data) => {
|
||||
documents.invoice = data
|
||||
documents.address = createDocumentResource({
|
||||
doctype: 'Address',
|
||||
name: data.supplier_address,
|
||||
auto: true
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -16,13 +16,7 @@
|
|||
<router-link to="/invoices/new">
|
||||
<button type="button" class="btn btn-primary btn-dark">New</button>
|
||||
</router-link>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-dark"
|
||||
@click="session.logout.submit"
|
||||
>
|
||||
Logout
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-dark" @click="session.logout.submit">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -52,7 +46,7 @@
|
|||
<td>
|
||||
<Badge>{{ item.status }}</Badge>
|
||||
</td>
|
||||
<td><i class="bi bi-currency-dollar"></i> {{ item.total_amount }}</td>
|
||||
<td><i class="bi bi-currency-dollar"></i> {{ item.grand_total }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -76,7 +70,7 @@ onBeforeMount(() => {
|
|||
})
|
||||
|
||||
let invoiceParams = {
|
||||
doctype: 'Invoices',
|
||||
doctype: 'Purchase Invoice',
|
||||
fields: ['*'],
|
||||
orderBy: 'creation desc',
|
||||
start: 0,
|
||||
|
|
|
|||
|
|
@ -3,49 +3,49 @@ import { session } from './data/session'
|
|||
import { userResource } from '@/data/user'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
name: 'Login',
|
||||
path: '/login',
|
||||
component: () => import('@/pages/Login.vue'),
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoiceList',
|
||||
path: '/',
|
||||
component: () => import('@/pages/SupplierInvoiceList.vue'),
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoice',
|
||||
path: '/invoices/:supplierInvoiceNumber',
|
||||
component: () => import('@/pages/SupplierInvoice.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoiceCreate',
|
||||
path: '/invoices/new',
|
||||
component: () => import('@/pages/NewInvoice.vue'),
|
||||
},
|
||||
{
|
||||
name: 'Login',
|
||||
path: '/login',
|
||||
component: () => import('@/pages/Login.vue'),
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoiceList',
|
||||
path: '/',
|
||||
component: () => import('@/pages/SupplierInvoiceList.vue'),
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoice',
|
||||
path: '/invoices/:supplierInvoiceNumber',
|
||||
component: () => import('@/pages/SupplierInvoice.vue'),
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
name: 'SupplierInvoiceCreate',
|
||||
path: '/invoices/new',
|
||||
component: () => import('@/pages/NewInvoice.vue'),
|
||||
},
|
||||
]
|
||||
|
||||
let router = createRouter({
|
||||
history: createWebHistory('/supplier-portal'),
|
||||
routes,
|
||||
history: createWebHistory('/supplier-portal'),
|
||||
routes,
|
||||
})
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
let isLoggedIn = session.isLoggedIn
|
||||
try {
|
||||
await userResource.promise
|
||||
} catch (error) {
|
||||
isLoggedIn = false
|
||||
}
|
||||
let isLoggedIn = session.isLoggedIn
|
||||
try {
|
||||
await userResource.promise
|
||||
} catch (error) {
|
||||
isLoggedIn = false
|
||||
}
|
||||
|
||||
if (to.name === 'Login' && isLoggedIn) {
|
||||
next({ name: 'SupplierInvoiceList' })
|
||||
} else if (to.name !== 'Login' && !isLoggedIn) {
|
||||
next({ name: 'Login' })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
if (to.name === 'Login' && isLoggedIn) {
|
||||
next({ name: 'SupplierInvoiceList' })
|
||||
} else if (to.name !== 'Login' && !isLoggedIn) {
|
||||
next({ name: 'Login' })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
export default router
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
require('frappe-ui/src/utils/tailwind.config')
|
||||
],
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
"./node_modules/frappe-ui/src/components/**/*.{vue,js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
container: {
|
||||
center: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
presets: [require('frappe-ui/src/utils/tailwind.config')],
|
||||
content: [
|
||||
'./index.html',
|
||||
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
'./node_modules/frappe-ui/src/components/**/*.{vue,js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
container: {
|
||||
center: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,18 +5,18 @@ import frappeui from 'frappe-ui/vite'
|
|||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [frappeui(), vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: `../${path.basename(path.resolve('..'))}/public/supplier-portal`,
|
||||
emptyOutDir: true,
|
||||
target: 'es2015',
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['frappe-ui > feather-icons', 'showdown', 'engine.io-client'],
|
||||
},
|
||||
})
|
||||
plugins: [frappeui(), vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
build: {
|
||||
outDir: `../${path.basename(path.resolve('..'))}/public/supplier-portal`,
|
||||
emptyOutDir: true,
|
||||
target: 'es2015',
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['frappe-ui > feather-icons', 'showdown', 'engine.io-client'],
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -137,31 +137,32 @@ 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.tasks.daily"
|
||||
# ],
|
||||
# "hourly": [
|
||||
# "suppliers_portal.tasks.hourly"
|
||||
# ],
|
||||
# "weekly": [
|
||||
# "suppliers_portal.tasks.weekly"
|
||||
# ],
|
||||
# "monthly": [
|
||||
# "suppliers_portal.tasks.monthly"
|
||||
# ],
|
||||
}
|
||||
|
||||
# Testing
|
||||
|
|
@ -242,6 +243,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"},
|
||||
]
|
||||
|
|
|
|||
47
suppliers_portal/overrides/supplier.py
Normal file
47
suppliers_portal/overrides/supplier.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import random
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def generate_supplier_id(supplier, method=None):
|
||||
# skip ID creation during patch migrations
|
||||
if frappe.flags.in_patch:
|
||||
return
|
||||
|
||||
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
|
||||
|
|
|
|||
346
suppliers_portal/patches/move_invoices_to_erpnext.py
Normal file
346
suppliers_portal/patches/move_invoices_to_erpnext.py
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
import json
|
||||
|
||||
import frappe
|
||||
from erpnext.accounts.doctype.account.account import update_account_number
|
||||
from erpnext.accounts.utils import get_account_name
|
||||
from erpnext.setup.utils import enable_all_roles_and_domains
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
|
||||
from frappe.utils import getdate, update_progress_bar
|
||||
|
||||
######################## PATCH PREREQUISITES ########################
|
||||
# - This patch should be run on a fresh site with ERPNext installed (`suppliers_portal` should be installed after `erpnext`)
|
||||
# - The following files (containing exported doctype records) must be setup in the
|
||||
# site's private backups folder:
|
||||
# - users.json (the 'send_welcome_email' field should be set to 0 otherwise it'll
|
||||
# try to send welcome emails)
|
||||
# - suppliers.json (remove duplicate entry for 'Superior Backflow Services, LLC')
|
||||
# - invoices.json
|
||||
|
||||
|
||||
def execute():
|
||||
setup_site()
|
||||
setup_accounts()
|
||||
create_fiscal_years()
|
||||
create_roles()
|
||||
import_users()
|
||||
create_items()
|
||||
create_payment_terms_template()
|
||||
import_suppliers()
|
||||
import_invoices()
|
||||
|
||||
|
||||
def setup_site():
|
||||
print("Setting up site")
|
||||
frappe.clear_cache()
|
||||
today = getdate()
|
||||
setup_complete(
|
||||
{
|
||||
"currency": "USD",
|
||||
"full_name": "Administrator",
|
||||
"company_name": "Sea Haven Industries",
|
||||
"timezone": "America/New_York",
|
||||
"company_abbr": "SHI",
|
||||
"domains": ["Services"],
|
||||
"country": "United States",
|
||||
"fy_start_date": today.replace(month=1, day=1).isoformat(),
|
||||
"fy_end_date": today.replace(month=12, day=31).isoformat(),
|
||||
"language": "en-US",
|
||||
"company_tagline": "Sea Haven Industries",
|
||||
"email": "support@seahavenindustries.com",
|
||||
"password": "admin",
|
||||
"chart_of_accounts": "Standard with Numbers",
|
||||
"bank_account": "Primary Checking",
|
||||
}
|
||||
)
|
||||
enable_all_roles_and_domains()
|
||||
for module in frappe.get_all("Module Onboarding"):
|
||||
frappe.db.set_value("Module Onboarding", module, "is_complete", True)
|
||||
frappe.db.set_value("User", "Administrator", "time_zone", "America/New_York")
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
def setup_accounts():
|
||||
abbr = frappe.db.get_value("Company", "Sea Haven Industries", "abbr")
|
||||
|
||||
frappe.rename_doc(
|
||||
"Account",
|
||||
f"1000 - Application of Funds (Assets) - {abbr}",
|
||||
f"1000 - Assets - {abbr}",
|
||||
force=True,
|
||||
)
|
||||
frappe.rename_doc(
|
||||
"Account",
|
||||
f"2000 - Source of Funds (Liabilities) - {abbr}",
|
||||
f"2000 - Liabilities - {abbr}",
|
||||
force=True,
|
||||
)
|
||||
frappe.rename_doc(
|
||||
"Account",
|
||||
f"1310 - Debtors - {abbr}",
|
||||
f"1310 - Accounts Receivable - {abbr}",
|
||||
force=True,
|
||||
)
|
||||
frappe.rename_doc(
|
||||
"Account",
|
||||
f"2110 - Creditors - {abbr}",
|
||||
f"2110 - Accounts Payable - {abbr}",
|
||||
force=True,
|
||||
)
|
||||
update_account_number(f"1110 - Cash - {abbr}", "Petty Cash", account_number="1110")
|
||||
update_account_number(
|
||||
f"Primary Checking - {abbr}", "Primary Checking", account_number="1201"
|
||||
)
|
||||
|
||||
|
||||
def create_fiscal_years():
|
||||
for year in range(2020, 2027):
|
||||
if not frappe.db.exists("Fiscal Year", str(year)):
|
||||
fiscal_year = frappe.new_doc("Fiscal Year")
|
||||
fiscal_year.update(
|
||||
{
|
||||
"year": str(year),
|
||||
"year_start_date": getdate(f"{year}-01-01"),
|
||||
"year_end_date": getdate(f"{year}-12-31"),
|
||||
}
|
||||
)
|
||||
fiscal_year.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def create_roles():
|
||||
roles = ["Portal Supplier", "Supplier"]
|
||||
for role in roles:
|
||||
if not frappe.db.exists("Role", role):
|
||||
role_doc = frappe.new_doc("Role")
|
||||
role_doc.update(
|
||||
{
|
||||
"role_name": role,
|
||||
"desk_access": False,
|
||||
}
|
||||
)
|
||||
role_doc.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def import_users():
|
||||
print("Importing users")
|
||||
users_file = frappe.get_site_path("private", "backups", "users.json")
|
||||
frappe.import_doc(users_file)
|
||||
|
||||
|
||||
def create_items():
|
||||
for item in ["Preventive Maintence", "Repair"]:
|
||||
if not frappe.db.exists("Item", item):
|
||||
item_doc = frappe.new_doc("Item")
|
||||
item_doc.update(
|
||||
{
|
||||
"item_code": item,
|
||||
"item_name": item,
|
||||
"item_group": "Services",
|
||||
"stock_uom": "Unit",
|
||||
}
|
||||
)
|
||||
item_doc.insert(ignore_permissions=True)
|
||||
|
||||
|
||||
def create_payment_terms_template():
|
||||
for credit_days in [0, 10, 15, 30, 45, 60]:
|
||||
template_name = f"NET {credit_days}" if credit_days else "Due On Receipt"
|
||||
|
||||
if not frappe.db.exists("Payment Terms Template", template_name):
|
||||
term = frappe.new_doc("Payment Term")
|
||||
term.update(
|
||||
{
|
||||
"payment_term_name": template_name,
|
||||
"invoice_portion": 100,
|
||||
"due_date_based_on": "Day(s) after invoice date",
|
||||
"credit_days": credit_days,
|
||||
}
|
||||
)
|
||||
term.save(ignore_permissions=True)
|
||||
|
||||
template = frappe.new_doc("Payment Terms Template")
|
||||
template.template_name = template_name
|
||||
template.append(
|
||||
"terms",
|
||||
{
|
||||
"payment_term": term.name,
|
||||
"invoice_portion": term.invoice_portion,
|
||||
"due_date_based_on": term.due_date_based_on,
|
||||
"credit_days": term.credit_days,
|
||||
},
|
||||
)
|
||||
template.save(ignore_permissions=True)
|
||||
|
||||
|
||||
def create_address(doc):
|
||||
address = frappe.new_doc("Address")
|
||||
address.update(
|
||||
{
|
||||
"address_type": "Billing",
|
||||
"address_title": doc.get("title"),
|
||||
"address_line1": doc.get("street"),
|
||||
"city": doc.get("city") or "Unknown",
|
||||
"state": doc.get("state"),
|
||||
"pincode": doc.get("zip_code"),
|
||||
"country": "United States",
|
||||
"is_primary_address": True,
|
||||
"is_shipping_address": True,
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": doc.get("ref_doctype"),
|
||||
"link_name": doc.get("ref_name"),
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
address.insert(ignore_permissions=True)
|
||||
except Exception as e:
|
||||
print(f"Error inserting address for {doc.get('ref_name')}: {e}")
|
||||
return frappe._dict()
|
||||
|
||||
return address
|
||||
|
||||
|
||||
def import_suppliers():
|
||||
suppliers_file = frappe.get_site_path("private", "backups", "suppliers.json")
|
||||
with open(suppliers_file) as f:
|
||||
suppliers = json.load(f)
|
||||
for idx, supplier in enumerate(suppliers):
|
||||
update_progress_bar("Importing suppliers", idx, (len(suppliers)))
|
||||
|
||||
# skip if supplier already exists
|
||||
if frappe.db.exists("Supplier", {"name": supplier.get("name")}):
|
||||
continue
|
||||
|
||||
supplier_doc = frappe.new_doc("Supplier")
|
||||
supplier_doc.update(
|
||||
{
|
||||
"supplier_name": supplier.get("company_name"),
|
||||
"portal_users": [{"user": supplier.get("user")}],
|
||||
"owner": supplier.get("owner"),
|
||||
"creation": supplier.get("creation"),
|
||||
}
|
||||
)
|
||||
supplier_doc.insert(ignore_permissions=True)
|
||||
supplier_docname = frappe.rename_doc(
|
||||
"Supplier", supplier_doc.name, supplier.get("name"), force=True
|
||||
)
|
||||
|
||||
if supplier.get("street"):
|
||||
address = create_address(
|
||||
{
|
||||
**supplier,
|
||||
"title": supplier.get("company_name"),
|
||||
"ref_doctype": "Supplier",
|
||||
"ref_name": supplier_docname,
|
||||
}
|
||||
)
|
||||
|
||||
if address:
|
||||
address_display = get_address_display(address.name)
|
||||
supplier_doc.db_set("supplier_primary_address", address.name)
|
||||
supplier_doc.db_set("primary_address", address_display)
|
||||
|
||||
|
||||
def import_invoices():
|
||||
invoices_file = frappe.get_site_path("private", "backups", "invoices.json")
|
||||
with open(invoices_file) as f:
|
||||
invoices = json.load(f)
|
||||
for idx, invoice in enumerate(invoices):
|
||||
update_progress_bar("Importing invoices", idx, (len(invoices)))
|
||||
|
||||
# skip if purchase invoice already exists
|
||||
if frappe.db.exists("Purchase Invoice", {"name": invoice.get("name")}):
|
||||
continue
|
||||
|
||||
invoice_doc = frappe.new_doc("Purchase Invoice")
|
||||
invoice_doc.update(
|
||||
{
|
||||
"supplier": invoice.get("supplier"),
|
||||
"bill_no": invoice.get("supplier_invoice_number"),
|
||||
"site_code": invoice.get("site_code"),
|
||||
"set_posting_time": True,
|
||||
"bill_date": getdate(invoice.get("invoice_date")),
|
||||
"posting_date": getdate(invoice.get("invoice_date")),
|
||||
"service_date": getdate(invoice.get("service_date")),
|
||||
"due_date": getdate(invoice.get("due_date")),
|
||||
"payment_terms_template": invoice.get("invoice_terms"),
|
||||
"remarks": invoice.get("notes"),
|
||||
"owner": invoice.get("owner"),
|
||||
"creation": invoice.get("creation"),
|
||||
}
|
||||
)
|
||||
|
||||
for item in invoice.get("supplier_invoice_items"):
|
||||
invoice_doc.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": item.get("service_type"),
|
||||
"received_qty": item.get("quantity"),
|
||||
"qty": item.get("quantity"),
|
||||
"rate": item.get("rate"),
|
||||
},
|
||||
)
|
||||
|
||||
# to avoid validating the custom due dates based on payment terms
|
||||
invoice_doc.ignore_default_payment_terms_template = True
|
||||
|
||||
try:
|
||||
invoice_doc.insert(ignore_permissions=True)
|
||||
except Exception as e:
|
||||
print(f"Error inserting invoice {invoice_doc.bill_no}: {e}")
|
||||
continue
|
||||
|
||||
if invoice.get("street"):
|
||||
address = create_address(
|
||||
{
|
||||
**invoice,
|
||||
"title": invoice.get("supplier"),
|
||||
"ref_doctype": "Supplier",
|
||||
"ref_name": invoice.get("supplier"),
|
||||
}
|
||||
)
|
||||
invoice_doc.supplier_address = address.name
|
||||
invoice_doc.save()
|
||||
|
||||
try:
|
||||
invoice_doc.submit()
|
||||
except Exception as e:
|
||||
print(f"Error submitting invoice {invoice_doc.bill_no}: {e}")
|
||||
continue
|
||||
|
||||
for payment in invoice.get("payment_references"):
|
||||
payment_doc = frappe.new_doc("Payment Entry")
|
||||
payment_doc.update(
|
||||
{
|
||||
"payment_type": "Pay",
|
||||
"posting_date": getdate(payment.get("payment_reference_date")),
|
||||
"party_type": "Supplier",
|
||||
"party": invoice.get("supplier"),
|
||||
"paid_amount": payment.get("paid_amount"),
|
||||
"received_amount": payment.get("paid_amount"),
|
||||
"paid_from": get_account_name("Bank", "Asset"),
|
||||
"reference_no": payment.get("payment_reference_number")
|
||||
or "Note: Missing reference during ERPNext import",
|
||||
"reference_date": getdate(
|
||||
payment.get("payment_reference_date")
|
||||
),
|
||||
"references": [
|
||||
{
|
||||
"reference_doctype": invoice_doc.doctype,
|
||||
"reference_name": invoice_doc.name,
|
||||
"allocated_amount": payment.get("paid_amount"),
|
||||
}
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
try:
|
||||
payment_doc.insert(ignore_permissions=True)
|
||||
payment_doc.submit()
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Error importing payment for invoice {invoice_doc.bill_no}: {e}"
|
||||
)
|
||||
|
|
@ -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 +1 @@
|
|||
import{_ as x,R as g,S as _,N as l,w as r,g as c,o,T as p,U as C,V as k,m as a,e as d,z as u,W as m,F as h,G as b}from"./vendor.99304cc5.js";const v={name:"FontColor",props:["editor"],components:{Popover:g,Tooltip:_},methods:{setBackgroundColor(t){t.name!="Default"?this.editor.chain().focus().toggleHighlight({color:t.hex}).run():this.editor.chain().focus().unsetHighlight().run()},setForegroundColor(t){t.name!="Default"?this.editor.chain().focus().setColor(t.hex).run():this.editor.chain().focus().unsetColor().run()}},computed:{foregroundColors(){return[{name:"Default",hex:"#1F272E"},{name:"Yellow",hex:"#ca8a04"},{name:"Orange",hex:"#ea580c"},{name:"Red",hex:"#dc2626"},{name:"Green",hex:"#16a34a"},{name:"Blue",hex:"#1579D0"},{name:"Purple",hex:"#9333ea"},{name:"Pink",hex:"#db2777"}]},backgroundColors(){return[{name:"Default",hex:null},{name:"Yellow",hex:"#fef9c3"},{name:"Orange",hex:"#ffedd5"},{name:"Red",hex:"#fee2e2"},{name:"Green",hex:"#dcfce7"},{name:"Blue",hex:"#D3E9FC"},{name:"Purple",hex:"#f3e8ff"},{name:"Pink",hex:"#fce7f3"}]}}},y={class:"p-2"},B=a("div",{class:"text-sm text-gray-700"},"Text Color",-1),P={class:"mt-1 grid grid-cols-8 gap-1"},F=["aria-label","onClick"],w=a("div",{class:"mt-2 text-sm text-gray-700"},"Background Color",-1),D={class:"mt-1 grid grid-cols-8 gap-1"},T=["aria-label","onClick"];function z(t,R,$,j,A,n){const i=c("Tooltip"),f=c("Popover");return o(),l(f,{transition:"default"},{target:r(({togglePopover:e,isOpen:s})=>[p(t.$slots,"default",C(k({onClick:()=>e(),isActive:s})))]),"body-main":r(()=>[a("div",y,[B,a("div",P,[(o(!0),d(h,null,u(n.foregroundColors,e=>(o(),l(i,{class:"flex",key:e.name,text:e.name},{default:r(()=>[a("button",{"aria-label":e.name,class:"flex h-5 w-5 items-center justify-center rounded border text-base",style:m({color:e.hex}),onClick:s=>n.setForegroundColor(e)}," A ",12,F)]),_:2},1032,["text"]))),128))]),w,a("div",D,[(o(!0),d(h,null,u(n.backgroundColors,e=>(o(),l(i,{class:"flex",key:e.name,text:e.name},{default:r(()=>[a("button",{"aria-label":e.name,class:b(["flex h-5 w-5 items-center justify-center rounded border text-base text-gray-900",e.hex?"border-transparent":"border-gray-200"]),style:m({backgroundColor:e.hex}),onClick:s=>n.setBackgroundColor(e)}," A ",14,T)]),_:2},1032,["text"]))),128))])])]),_:3})}var G=x(v,[["render",z]]);export{G as default};
|
||||
import{_ as x,R as g,S as _,N as l,w as r,g as d,o,T as p,U as C,V as b,m as a,e as c,z as u,W as m,F as h,G as k}from"./vendor.3e69a9be.js";const v={name:"FontColor",props:["editor"],components:{Popover:g,Tooltip:_},methods:{setBackgroundColor(t){t.name!="Default"?this.editor.chain().focus().toggleHighlight({color:t.hex}).run():this.editor.chain().focus().unsetHighlight().run()},setForegroundColor(t){t.name!="Default"?this.editor.chain().focus().setColor(t.hex).run():this.editor.chain().focus().unsetColor().run()}},computed:{foregroundColors(){return[{name:"Default",hex:"#1F272E"},{name:"Yellow",hex:"#ca8a04"},{name:"Orange",hex:"#ea580c"},{name:"Red",hex:"#dc2626"},{name:"Green",hex:"#16a34a"},{name:"Blue",hex:"#1579D0"},{name:"Purple",hex:"#9333ea"},{name:"Pink",hex:"#db2777"}]},backgroundColors(){return[{name:"Default",hex:null},{name:"Yellow",hex:"#fef9c3"},{name:"Orange",hex:"#ffedd5"},{name:"Red",hex:"#fee2e2"},{name:"Green",hex:"#dcfce7"},{name:"Blue",hex:"#D3E9FC"},{name:"Purple",hex:"#f3e8ff"},{name:"Pink",hex:"#fce7f3"}]}}},y={class:"p-2"},B=a("div",{class:"text-sm text-gray-700"},"Text Color",-1),P={class:"mt-1 grid grid-cols-8 gap-1"},F=["aria-label","onClick"],w=a("div",{class:"mt-2 text-sm text-gray-700"},"Background Color",-1),D={class:"mt-1 grid grid-cols-8 gap-1"},T=["aria-label","onClick"];function z(t,R,$,j,A,n){const i=d("Tooltip"),f=d("Popover");return o(),l(f,{transition:"default"},{target:r(({togglePopover:e,isOpen:s})=>[p(t.$slots,"default",C(b({onClick:()=>e(),isActive:s})))]),"body-main":r(()=>[a("div",y,[B,a("div",P,[(o(!0),c(h,null,u(n.foregroundColors,e=>(o(),l(i,{class:"flex",key:e.name,text:e.name},{default:r(()=>[a("button",{"aria-label":e.name,class:"flex h-5 w-5 items-center justify-center rounded border text-base",style:m({color:e.hex}),onClick:s=>n.setForegroundColor(e)}," A ",12,F)]),_:2},1032,["text"]))),128))]),w,a("div",D,[(o(!0),c(h,null,u(n.backgroundColors,e=>(o(),l(i,{class:"flex",key:e.name,text:e.name},{default:r(()=>[a("button",{"aria-label":e.name,class:k(["flex h-5 w-5 items-center justify-center rounded border text-base text-gray-900",e.hex?"border-transparent":"border-gray-200"]),style:m({backgroundColor:e.hex}),onClick:s=>n.setBackgroundColor(e)}," A ",14,T)]),_:2},1032,["text"]))),128))])])]),_:3})}var G=x(v,[["render",z]]);export{G as default};
|
||||
|
|
@ -1 +1 @@
|
|||
import{_ as f,j as I,Y as _,$ as h,e as g,T as D,U as v,V as y,f as n,w as l,F as w,g as m,o as c,m as i,t as C,p as V,v as u}from"./vendor.99304cc5.js";const b={name:"InsertImage",props:["editor"],expose:["openDialog"],data(){return{addImageDialog:{url:"",file:null,show:!1}}},components:{Button:I,Dialog:_},methods:{openDialog(){this.addImageDialog.show=!0},onImageSelect(t){let e=t.target.files[0];!e||(this.addImageDialog.file=e,h(e).then(r=>{this.addImageDialog.url=r}))},addImage(t){this.editor.chain().focus().setImage({src:t}).run(),this.reset()},reset(){this.addImageDialog=this.$options.data().addImageDialog}}},k={class:"relative cursor-pointer rounded-lg bg-gray-100 py-1 focus-within:bg-gray-200 hover:bg-gray-200"},x={class:"absolute inset-0 select-none px-2 py-1 text-base"},B=["src"],S=u(" Insert Image "),N=u(" Cancel ");function A(t,e,r,T,a,o){const d=m("Button"),p=m("Dialog");return c(),g(w,null,[D(t.$slots,"default",v(y({onClick:o.openDialog}))),n(p,{options:{title:"Add Image"},modelValue:a.addImageDialog.show,"onUpdate:modelValue":e[2]||(e[2]=s=>a.addImageDialog.show=s),onAfterLeave:o.reset},{"body-content":l(()=>[i("label",k,[i("input",{type:"file",class:"w-full opacity-0",onChange:e[0]||(e[0]=(...s)=>o.onImageSelect&&o.onImageSelect(...s)),accept:"image/*"},null,32),i("span",x,C(a.addImageDialog.file?"Select another image":"Select an image"),1)]),a.addImageDialog.url?(c(),g("img",{key:0,src:a.addImageDialog.url,class:"mt-2 w-full rounded-lg"},null,8,B)):V("",!0)]),actions:l(()=>[n(d,{variant:"solid",onClick:e[1]||(e[1]=s=>o.addImage(a.addImageDialog.url))},{default:l(()=>[S]),_:1}),n(d,{onClick:o.reset},{default:l(()=>[N]),_:1},8,["onClick"])]),_:1},8,["modelValue","onAfterLeave"])],64)}var F=f(b,[["render",A]]);export{F as default};
|
||||
import{_ as f,j as I,Y as _,$ as h,e as g,T as D,U as v,V as y,f as n,w as l,F as w,g as m,o as c,m as i,t as C,p as b,v as u}from"./vendor.3e69a9be.js";const V={name:"InsertImage",props:["editor"],expose:["openDialog"],data(){return{addImageDialog:{url:"",file:null,show:!1}}},components:{Button:I,Dialog:_},methods:{openDialog(){this.addImageDialog.show=!0},onImageSelect(t){let e=t.target.files[0];!e||(this.addImageDialog.file=e,h(e).then(r=>{this.addImageDialog.url=r}))},addImage(t){this.editor.chain().focus().setImage({src:t}).run(),this.reset()},reset(){this.addImageDialog=this.$options.data().addImageDialog}}},k={class:"relative cursor-pointer rounded-lg bg-gray-100 py-1 focus-within:bg-gray-200 hover:bg-gray-200"},x={class:"absolute inset-0 select-none px-2 py-1 text-base"},B=["src"],S=u(" Insert Image "),N=u(" Cancel ");function A(t,e,r,T,a,o){const d=m("Button"),p=m("Dialog");return c(),g(w,null,[D(t.$slots,"default",v(y({onClick:o.openDialog}))),n(p,{options:{title:"Add Image"},modelValue:a.addImageDialog.show,"onUpdate:modelValue":e[2]||(e[2]=s=>a.addImageDialog.show=s),onAfterLeave:o.reset},{"body-content":l(()=>[i("label",k,[i("input",{type:"file",class:"w-full opacity-0",onChange:e[0]||(e[0]=(...s)=>o.onImageSelect&&o.onImageSelect(...s)),accept:"image/*"},null,32),i("span",x,C(a.addImageDialog.file?"Select another image":"Select an image"),1)]),a.addImageDialog.url?(c(),g("img",{key:0,src:a.addImageDialog.url,class:"mt-2 w-full rounded-lg"},null,8,B)):b("",!0)]),actions:l(()=>[n(d,{variant:"solid",onClick:e[1]||(e[1]=s=>o.addImage(a.addImageDialog.url))},{default:l(()=>[S]),_:1}),n(d,{onClick:o.reset},{default:l(()=>[N]),_:1},8,["onClick"])]),_:1},8,["modelValue","onAfterLeave"])],64)}var F=f(V,[["render",A]]);export{F as default};
|
||||
|
|
@ -1 +1 @@
|
|||
import{_ as d,j as g,X as m,Y as L,e as f,T as p,U as c,V as D,f as i,w as l,F as h,g as a,o as _,Z as v,v as w}from"./vendor.99304cc5.js";const V={name:"InsertLink",props:["editor"],components:{Button:g,FormControl:m,Dialog:L},data(){return{setLinkDialog:{url:"",show:!1}}},methods:{openDialog(){let t=this.editor.getAttributes("link").href;t&&(this.setLinkDialog.url=t),this.setLinkDialog.show=!0},setLink(t){t===""?this.editor.chain().focus().extendMarkRange("link").unsetLink().run():this.editor.chain().focus().extendMarkRange("link").setLink({href:t}).run(),this.setLinkDialog.show=!1,this.setLinkDialog.url=""},reset(){this.setLinkDialog=this.$options.data().setLinkDialog}}},x=w(" Save ");function C(t,e,B,F,n,s){const r=a("FormControl"),u=a("Button"),k=a("Dialog");return _(),f(h,null,[p(t.$slots,"default",c(D({onClick:s.openDialog}))),i(k,{options:{title:"Set Link"},modelValue:n.setLinkDialog.show,"onUpdate:modelValue":e[3]||(e[3]=o=>n.setLinkDialog.show=o),onAfterLeave:s.reset},{"body-content":l(()=>[i(r,{type:"text",label:"URL",modelValue:n.setLinkDialog.url,"onUpdate:modelValue":e[0]||(e[0]=o=>n.setLinkDialog.url=o),onKeydown:e[1]||(e[1]=v(o=>s.setLink(o.target.value),["enter"]))},null,8,["modelValue"])]),actions:l(()=>[i(u,{variant:"solid",onClick:e[2]||(e[2]=o=>s.setLink(n.setLinkDialog.url))},{default:l(()=>[x]),_:1})]),_:1},8,["modelValue","onAfterLeave"])],64)}var U=d(V,[["render",C]]);export{U as default};
|
||||
import{_ as d,j as g,X as m,Y as L,e as f,T as p,U as D,V as c,f as i,w as l,F as h,g as a,o as _,Z as v,v as w}from"./vendor.3e69a9be.js";const V={name:"InsertLink",props:["editor"],components:{Button:g,FormControl:m,Dialog:L},data(){return{setLinkDialog:{url:"",show:!1}}},methods:{openDialog(){let t=this.editor.getAttributes("link").href;t&&(this.setLinkDialog.url=t),this.setLinkDialog.show=!0},setLink(t){t===""?this.editor.chain().focus().extendMarkRange("link").unsetLink().run():this.editor.chain().focus().extendMarkRange("link").setLink({href:t}).run(),this.setLinkDialog.show=!1,this.setLinkDialog.url=""},reset(){this.setLinkDialog=this.$options.data().setLinkDialog}}},x=w(" Save ");function C(t,e,B,F,n,s){const r=a("FormControl"),u=a("Button"),k=a("Dialog");return _(),f(h,null,[p(t.$slots,"default",D(c({onClick:s.openDialog}))),i(k,{options:{title:"Set Link"},modelValue:n.setLinkDialog.show,"onUpdate:modelValue":e[3]||(e[3]=o=>n.setLinkDialog.show=o),onAfterLeave:s.reset},{"body-content":l(()=>[i(r,{type:"text",label:"URL",modelValue:n.setLinkDialog.url,"onUpdate:modelValue":e[0]||(e[0]=o=>n.setLinkDialog.url=o),onKeydown:e[1]||(e[1]=v(o=>s.setLink(o.target.value),["enter"]))},null,8,["modelValue"])]),actions:l(()=>[i(u,{variant:"solid",onClick:e[2]||(e[2]=o=>s.setLink(n.setLinkDialog.url))},{default:l(()=>[x]),_:1})]),_:1},8,["modelValue","onAfterLeave"])],64)}var U=d(V,[["render",C]]);export{U as default};
|
||||
|
|
@ -1 +1 @@
|
|||
import{_ as D,j as v,Y as h,a0 as C,e as u,T as k,U as w,V as x,f as t,w as i,F as B,g as r,o as c,m as U,v as n,t as y,N as F,p}from"./vendor.99304cc5.js";const N={name:"InsertImage",props:["editor"],expose:["openDialog"],data(){return{addVideoDialog:{url:"",file:null,show:!1}}},components:{Button:v,Dialog:h,FileUploader:C},methods:{openDialog(){this.addVideoDialog.show=!0},onVideoSelect(l){let o=l.target.files[0];!o||(this.addVideoDialog.file=o)},addVideo(l){this.editor.chain().focus().insertContent(`<video src="${l}"></video>`).run(),this.reset()},reset(){this.addVideoDialog=this.$options.data().addVideoDialog}}},I={class:"flex items-center space-x-2"},S=n(" Remove "),A=["src"],j=n(" Insert Video "),L=n("Cancel");function P(l,o,R,T,e,s){const a=r("Button"),V=r("FileUploader"),g=r("Dialog");return c(),u(B,null,[k(l.$slots,"default",w(x({onClick:s.openDialog}))),t(g,{options:{title:"Add Video"},modelValue:e.addVideoDialog.show,"onUpdate:modelValue":o[2]||(o[2]=d=>e.addVideoDialog.show=d),onAfterLeave:s.reset},{"body-content":i(()=>[t(V,{"file-types":"video/*",onSuccess:o[0]||(o[0]=d=>e.addVideoDialog.url=d.file_url)},{default:i(({file:d,progress:f,uploading:_,openFileSelector:m})=>[U("div",I,[t(a,{onClick:m},{default:i(()=>[n(y(_?`Uploading ${f}%`:e.addVideoDialog.url?"Change Video":"Upload Video"),1)]),_:2},1032,["onClick"]),e.addVideoDialog.url?(c(),F(a,{key:0,onClick:()=>{e.addVideoDialog.url=null,e.addVideoDialog.file=null}},{default:i(()=>[S]),_:2},1032,["onClick"])):p("",!0)])]),_:1}),e.addVideoDialog.url?(c(),u("video",{key:0,src:e.addVideoDialog.url,class:"mt-2 w-full rounded-lg",type:"video/mp4",controls:""},null,8,A)):p("",!0)]),actions:i(()=>[t(a,{variant:"solid",onClick:o[1]||(o[1]=d=>s.addVideo(e.addVideoDialog.url))},{default:i(()=>[j]),_:1}),t(a,{onClick:s.reset},{default:i(()=>[L]),_:1},8,["onClick"])]),_:1},8,["modelValue","onAfterLeave"])],64)}var z=D(N,[["render",P]]);export{z as default};
|
||||
import{_ as D,j as v,Y as h,a0 as C,e as u,T as k,U as w,V as x,f as t,w as i,F as B,g as r,o as c,m as U,v as n,t as y,N as F,p}from"./vendor.3e69a9be.js";const N={name:"InsertImage",props:["editor"],expose:["openDialog"],data(){return{addVideoDialog:{url:"",file:null,show:!1}}},components:{Button:v,Dialog:h,FileUploader:C},methods:{openDialog(){this.addVideoDialog.show=!0},onVideoSelect(l){let o=l.target.files[0];!o||(this.addVideoDialog.file=o)},addVideo(l){this.editor.chain().focus().insertContent(`<video src="${l}"></video>`).run(),this.reset()},reset(){this.addVideoDialog=this.$options.data().addVideoDialog}}},I={class:"flex items-center space-x-2"},S=n(" Remove "),A=["src"],b=n(" Insert Video "),j=n("Cancel");function L(l,o,P,R,e,a){const s=r("Button"),V=r("FileUploader"),g=r("Dialog");return c(),u(B,null,[k(l.$slots,"default",w(x({onClick:a.openDialog}))),t(g,{options:{title:"Add Video"},modelValue:e.addVideoDialog.show,"onUpdate:modelValue":o[2]||(o[2]=d=>e.addVideoDialog.show=d),onAfterLeave:a.reset},{"body-content":i(()=>[t(V,{"file-types":"video/*",onSuccess:o[0]||(o[0]=d=>e.addVideoDialog.url=d.file_url)},{default:i(({file:d,progress:f,uploading:_,openFileSelector:m})=>[U("div",I,[t(s,{onClick:m},{default:i(()=>[n(y(_?`Uploading ${f}%`:e.addVideoDialog.url?"Change Video":"Upload Video"),1)]),_:2},1032,["onClick"]),e.addVideoDialog.url?(c(),F(s,{key:0,onClick:()=>{e.addVideoDialog.url=null,e.addVideoDialog.file=null}},{default:i(()=>[S]),_:2},1032,["onClick"])):p("",!0)])]),_:1}),e.addVideoDialog.url?(c(),u("video",{key:0,src:e.addVideoDialog.url,class:"mt-2 w-full rounded-lg",type:"video/mp4",controls:""},null,8,A)):p("",!0)]),actions:i(()=>[t(s,{variant:"solid",onClick:o[1]||(o[1]=d=>a.addVideo(e.addVideoDialog.url))},{default:i(()=>[b]),_:1}),t(s,{onClick:a.reset},{default:i(()=>[j]),_:1},8,["onClick"])]),_:1},8,["modelValue","onAfterLeave"])],64)}var z=D(N,[["render",L]]);export{z as default};
|
||||
|
|
@ -1 +1 @@
|
|||
import{l as f,r as g,e as r,f as o,w as m,g as i,o as n,m as w,n as x,u as t,t as h,p as y,q as b,v}from"./vendor.99304cc5.js";import{s}from"./index.87704181.js";const V={class:"m-3 flex flex-row items-center justify-center"},A=["onSubmit"],C={key:0},D={key:1},S=v("Login"),k={key:2,class:"text-red-500"},N=f({setup(B){const a=g({forAdmin:!1});function p(d){let e=new FormData(d.target);a.forAdmin?s.login.submit({email:e.get("email"),password:e.get("password")}):s.supplier_login.submit({supplier_id:e.get("supplier_id")})}return(d,e)=>{const l=i("Input"),u=i("Button"),c=i("Card");return n(),r("div",V,[o(c,{title:"Welcome to Supplier Portal!",class:"w-full max-w-md mt-4"},{default:m(()=>[w("form",{class:"flex flex-col space-y-2 w-full",onSubmit:b(p,["prevent"])},[o(x,{modelValue:t(a).forAdmin,"onUpdate:modelValue":e[0]||(e[0]=_=>t(a).forAdmin=_),label:"Login as Admin"},null,8,["modelValue"]),t(a).forAdmin?(n(),r("div",C,[o(l,{required:"",name:"email",type:"text",placeholder:"username@email.com",label:"User ID"}),o(l,{required:"",name:"password",type:"password",placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022",label:"Password"})])):(n(),r("div",D,[o(l,{required:"",name:"supplier_id",type:"text",placeholder:"Supplier ID",label:"Supplier ID"})])),o(u,{loading:t(s).login.loading,appearance:"primary"},{default:m(()=>[S]),_:1},8,["loading"]),t(s).login.error?(n(),r("div",k,h(t(s).login.error),1)):y("",!0)],40,A)]),_:1})])}}});export{N as default};
|
||||
import{l as f,r as g,e as r,f as o,w as m,g as i,o as n,m as w,n as x,u as t,t as b,p as h,q as y,v}from"./vendor.3e69a9be.js";import{s}from"./index.a459834f.js";const V={class:"m-3 flex flex-row items-center justify-center"},A=["onSubmit"],C={key:0},D={key:1},S=v("Login"),k={key:2,class:"text-red-500"},N=f({setup(B){const a=g({forAdmin:!1});function p(d){let e=new FormData(d.target);a.forAdmin?s.login.submit({email:e.get("email"),password:e.get("password")}):s.supplier_login.submit({supplier_id:e.get("supplier_id")})}return(d,e)=>{const l=i("Input"),u=i("Button"),c=i("Card");return n(),r("div",V,[o(c,{title:"Welcome to Supplier Portal!",class:"w-full max-w-md mt-4"},{default:m(()=>[w("form",{class:"flex flex-col space-y-2 w-full",onSubmit:y(p,["prevent"])},[o(x,{modelValue:t(a).forAdmin,"onUpdate:modelValue":e[0]||(e[0]=_=>t(a).forAdmin=_),label:"Login as Admin"},null,8,["modelValue"]),t(a).forAdmin?(n(),r("div",C,[o(l,{required:"",name:"email",type:"text",placeholder:"username@email.com",label:"User ID"}),o(l,{required:"",name:"password",type:"password",placeholder:"\u2022\u2022\u2022\u2022\u2022\u2022",label:"Password"})])):(n(),r("div",D,[o(l,{required:"",name:"supplier_id",type:"text",placeholder:"Supplier ID",label:"Supplier ID"})])),o(u,{loading:t(s).login.loading,appearance:"primary"},{default:m(()=>[S]),_:1},8,["loading"]),t(s).login.error?(n(),r("div",k,b(t(s).login.error),1)):h("",!0)],40,A)]),_:1})])}}});export{N as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
input.form-control-plaintext[data-v-7312333a]:focus,.currency-fmt[data-v-7312333a]{background-color:#fff;border-color:#fff;box-shadow:none}.currency-fmt+input.form-control[data-v-7312333a]{background-color:#fff;border-color:#fff;box-shadow:none}tr[data-v-7312333a]{vertical-align:middle}
|
||||
|
|
@ -0,0 +1 @@
|
|||
input.form-control-plaintext[data-v-0c596e92]:focus,.currency-fmt[data-v-0c596e92]{background-color:#fff;border-color:#fff;box-shadow:none}.currency-fmt+input.form-control[data-v-0c596e92]{background-color:#fff;border-color:#fff;box-shadow:none}tr[data-v-0c596e92]{vertical-align:middle}
|
||||
|
|
@ -0,0 +1 @@
|
|||
.vr-aligned[data-v-5c4ded13]{vertical-align:middle}button.accordion-button[aria-expanded=true][data-v-5c4ded13]{color:#000}input.form-control-plaintext[data-v-5c4ded13]:focus,.input-group>input.form-control[data-v-5c4ded13]:focus,input.form-control[data-v-5c4ded13],.input-group>span[data-v-5c4ded13]{background-color:#fff;border-color:#fff;box-shadow:none}.disabled-date[data-v-5c4ded13]{color:#000;background:transparent;border:none!important;border-bottom:1px solid #ced4da!important}a[data-v-5c4ded13]{text-decoration:none;color:inherit}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
.vr-aligned[data-v-145c3c9a]{vertical-align:middle}button.accordion-button[aria-expanded=true][data-v-145c3c9a]{color:#000}input.form-control-plaintext[data-v-145c3c9a]:focus,.input-group>input.form-control[data-v-145c3c9a]:focus,input.form-control[data-v-145c3c9a],.input-group>span[data-v-145c3c9a]{background-color:#fff;border-color:#fff;box-shadow:none}.disabled-date[data-v-145c3c9a]{color:#000;background:transparent;border:none!important;border-bottom:1px solid #ced4da!important}a[data-v-145c3c9a]{text-decoration:none;color:inherit}
|
||||
|
|
@ -0,0 +1 @@
|
|||
a[data-v-570396b7]{text-decoration:none;color:inherit}
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{_ as y,x as k,y as h,e as a,m as t,f as i,w as l,u as s,F as I,z as w,p as b,A as B,B as S,g,o as c,v as r,t as p,D as N}from"./vendor.99304cc5.js";import{s as d}from"./index.87704181.js";const _=u=>(B("data-v-6f6873f7"),u=u(),S(),u),L={class:"container-fluid"},C={class:"container mx-auto mt-5"},P={class:"row"},V=_(()=>t("div",{class:"col"},[t("h1",{class:"text-gray-900 font-bold text-[32px]"},"Invoices"),t("div",{class:"d-flex"},[t("div",null,[t("div",{class:"btn-group",role:"group","aria-label":"Basic example"})])])],-1)),j={class:"col"},$={class:"d-flex"},A={class:"ms-auto d-flex",style:{gap:"2px"}},D=_(()=>t("button",{type:"button",class:"btn btn-primary btn-dark"},"New",-1)),F={key:0,class:"container mx-auto mb-3 mt-5"},z={class:"table"},E=_(()=>t("thead",null,[t("tr",null,[t("th",{scope:"col"},"Invoice Number"),t("th",{scope:"col"},"Supplier"),t("th",{scope:"col"},"Status"),t("th",{scope:"col"},"Amount")])],-1)),M={class:"table-group-divider"},R=_(()=>t("i",{class:"bi bi-currency-dollar"},null,-1)),T={key:0,class:"flex justify-between mt-4"},q=r(" Previous Page "),G=r(" Next Page "),H={setup(u){k(()=>{window.location.origin==="https://accounting.seahavenind.com"&&window.location.replace("https://accounting.seahavenind.com/app")});let v={doctype:"Invoices",fields:["*"],orderBy:"creation desc",start:0,pageLength:5,auto:!0},o=null;return d.supplier_id&&d.isLoggedIn?(v.filters=[["supplier","=",d.supplier_id]],o=h(v)):o=h(v),o.fetch(),(J,n)=>{const m=g("router-link"),f=g("Button");return c(),a("div",L,[t("div",C,[t("div",P,[V,t("div",j,[t("div",$,[t("div",A,[i(m,{to:"/invoices/new"},{default:l(()=>[D]),_:1}),t("button",{type:"button",class:"btn btn-primary btn-dark",onClick:n[0]||(n[0]=(...e)=>s(d).logout.submit&&s(d).logout.submit(...e))}," Logout ")])])])]),s(o).data?(c(),a("div",F,[t("table",z,[E,t("tbody",M,[(c(!0),a(I,null,w(s(o).data,(e,x)=>(c(),a("tr",{key:x},[t("td",null,[i(m,{to:{name:"SupplierInvoice",params:{supplierInvoiceNumber:e.name}}},{default:l(()=>[r(p(e.name),1)]),_:2},1032,["to"])]),t("td",null,p(e.supplier),1),t("td",null,[i(s(N),null,{default:l(()=>[r(p(e.status),1)]),_:2},1024)]),t("td",null,[R,r(" "+p(e.total_amount),1)])]))),128))])])])):b("",!0)]),s(o).length>10?(c(),a("div",T,[i(f,{onClick:n[1]||(n[1]=e=>s(o).previous())},{default:l(()=>[q]),_:1}),i(f,{onClick:n[2]||(n[2]=e=>s(o).next())},{default:l(()=>[G]),_:1})])):b("",!0)])}}};var Q=y(H,[["__scopeId","data-v-6f6873f7"]]);export{Q as default};
|
||||
|
|
@ -0,0 +1 @@
|
|||
import{_ as y,x as k,y as h,e as a,m as t,f as i,w as l,u as o,F as I,z as w,p as f,A as B,B as S,g,o as c,v as r,t as p,D as N}from"./vendor.3e69a9be.js";import{s as d}from"./index.a459834f.js";const _=u=>(B("data-v-570396b7"),u=u(),S(),u),L={class:"container-fluid"},C={class:"container mx-auto mt-5"},P={class:"row"},V=_(()=>t("div",{class:"col"},[t("h1",{class:"text-gray-900 font-bold text-[32px]"},"Invoices"),t("div",{class:"d-flex"},[t("div",null,[t("div",{class:"btn-group",role:"group","aria-label":"Basic example"})])])],-1)),j={class:"col"},$={class:"d-flex"},A={class:"ms-auto d-flex",style:{gap:"2px"}},D=_(()=>t("button",{type:"button",class:"btn btn-primary btn-dark"},"New",-1)),F={key:0,class:"container mx-auto mb-3 mt-5"},z={class:"table"},E=_(()=>t("thead",null,[t("tr",null,[t("th",{scope:"col"},"Invoice Number"),t("th",{scope:"col"},"Supplier"),t("th",{scope:"col"},"Status"),t("th",{scope:"col"},"Amount")])],-1)),M={class:"table-group-divider"},R=_(()=>t("i",{class:"bi bi-currency-dollar"},null,-1)),T={key:0,class:"flex justify-between mt-4"},q=r(" Previous Page "),G=r(" Next Page "),H={setup(u){k(()=>{window.location.origin==="https://accounting.seahavenind.com"&&window.location.replace("https://accounting.seahavenind.com/app")});let v={doctype:"Purchase Invoice",fields:["*"],orderBy:"creation desc",start:0,pageLength:5,auto:!0},s=null;return d.supplier_id&&d.isLoggedIn?(v.filters=[["supplier","=",d.supplier_id]],s=h(v)):s=h(v),s.fetch(),(J,n)=>{const m=g("router-link"),b=g("Button");return c(),a("div",L,[t("div",C,[t("div",P,[V,t("div",j,[t("div",$,[t("div",A,[i(m,{to:"/invoices/new"},{default:l(()=>[D]),_:1}),t("button",{type:"button",class:"btn btn-primary btn-dark",onClick:n[0]||(n[0]=(...e)=>o(d).logout.submit&&o(d).logout.submit(...e))},"Logout")])])])]),o(s).data?(c(),a("div",F,[t("table",z,[E,t("tbody",M,[(c(!0),a(I,null,w(o(s).data,(e,x)=>(c(),a("tr",{key:x},[t("td",null,[i(m,{to:{name:"SupplierInvoice",params:{supplierInvoiceNumber:e.name}}},{default:l(()=>[r(p(e.name),1)]),_:2},1032,["to"])]),t("td",null,p(e.supplier),1),t("td",null,[i(o(N),null,{default:l(()=>[r(p(e.status),1)]),_:2},1024)]),t("td",null,[R,r(" "+p(e.grand_total),1)])]))),128))])])])):f("",!0)]),o(s).length>10?(c(),a("div",T,[i(b,{onClick:n[1]||(n[1]=e=>o(s).previous())},{default:l(()=>[q]),_:1}),i(b,{onClick:n[2]||(n[2]=e=>o(s).next())},{default:l(()=>[G]),_:1})])):f("",!0)])}}};var Q=y(H,[["__scopeId","data-v-570396b7"]]);export{Q as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
a[data-v-6f6873f7]{text-decoration:none;color:inherit}
|
||||
|
|
@ -1 +0,0 @@
|
|||
var h=(e,o,n)=>new Promise((s,r)=>{var t=p=>{try{a(n.next(p))}catch(g){r(g)}},c=p=>{try{a(n.throw(p))}catch(g){r(g)}},a=p=>p.done?s(p.value):Promise.resolve(p.value).then(t,c);a((n=n.apply(e,o)).next())});import{c as m,r as L,a as I,b as k,d as S,_ as y,e as E,f as P,g as b,o as C,h as R,s as T,i as w,j as $,C as U,I as j,k as O}from"./vendor.99304cc5.js";const A=function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const t of r)if(t.type==="childList")for(const c of t.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&s(c)}).observe(document,{childList:!0,subtree:!0});function n(r){const t={};return r.integrity&&(t.integrity=r.integrity),r.referrerpolicy&&(t.referrerPolicy=r.referrerpolicy),r.crossorigin==="use-credentials"?t.credentials="include":r.crossorigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function s(r){if(r.ep)return;r.ep=!0;const t=n(r);fetch(r.href,t)}};A();const x="modulepreload",v={},N="/assets/suppliers_portal/supplier-portal/",d=function(o,n){return!n||n.length===0?o():Promise.all(n.map(s=>{if(s=`${N}${s}`,s in v)return;v[s]=!0;const r=s.endsWith(".css"),t=r?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${t}`))return;const c=document.createElement("link");if(c.rel=r?"stylesheet":x,r||(c.as="script",c.crossOrigin=""),c.href=s,document.head.appendChild(c),r)return new Promise((a,p)=>{c.addEventListener("load",a),c.addEventListener("error",p)})})).then(()=>o())},_=m({url:"frappe.auth.get_logged_user",cache:"User",onError(e){e&&e.exc_type==="AuthenticationError"&&u.push({name:"LoginPage"})}});function f(){let o=new URLSearchParams(document.cookie.split("; ").join("&")).get("user_id");return o==="Guest"&&(o=null),o}function V(){return new URLSearchParams(document.cookie.split("; ").join("&")).get("supplier_id")}function D(){return new URLSearchParams(document.cookie.split("; ").join("&")).get("supplier_name")}const i=L({supplier_login:m({url:"suppliers_portal.suppliers_portal.api.validate_supplier_id",makeParams({supplier_id:e}){return{supplier_id:e}},onSuccess(e){e&&e.status==="success"&&(_.reload(),i.user=f(),document.cookie=`supplier_id=${e.supplier_id}`,document.cookie=`supplier_name=${e.supplier_name}`,i.supplier_id=e.supplier_id,i.supplier_name=e.supplier_name,i.login.reset(),u.replace({name:"SupplierInvoiceList"}))},onError(e){console.error("Login failed",e)}}),login:m({url:"login",makeParams({email:e,password:o}){return{usr:e,pwd:o}},onSuccess(e){e&&(_.reload(),i.user=f(),document.cookie=`supplier_id=${i.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,document.cookie=`supplier_id=${i.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,i.supplier_id="",i.supplier_name="",i.login.reset(),u.replace({name:"SupplierInvoiceList"}))},onError(e){console.error("Login failed",e)}}),logout:m({url:"logout",onSuccess(){document.cookie=`supplier_id=${i.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,document.cookie=`supplier_id=${i.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,i.supplier_id="",i.supplier_name="",_.reset(),i.user=f(),u.replace({name:"Login"})}}),user:f(),supplier_id:V(),supplier_name:D(),isLoggedIn:I(()=>!!i.user)}),J=[{name:"Login",path:"/login",component:()=>d(()=>import("./Login.14c1625b.js"),["assets/Login.14c1625b.js","assets/vendor.99304cc5.js","assets/vendor.52e2d7c1.css"])},{name:"SupplierInvoiceList",path:"/",component:()=>d(()=>import("./SupplierInvoiceList.bd20c4e8.js"),["assets/SupplierInvoiceList.bd20c4e8.js","assets/SupplierInvoiceList.e8de1e21.css","assets/vendor.99304cc5.js","assets/vendor.52e2d7c1.css"])},{name:"SupplierInvoice",path:"/invoices/:supplierInvoiceNumber",component:()=>d(()=>import("./SupplierInvoice.77908b92.js"),["assets/SupplierInvoice.77908b92.js","assets/SupplierInvoice.9dfb3ca8.css","assets/vendor.99304cc5.js","assets/vendor.52e2d7c1.css"]),props:!0},{name:"SupplierInvoiceCreate",path:"/invoices/new",component:()=>d(()=>import("./NewInvoice.632d4fad.js"),["assets/NewInvoice.632d4fad.js","assets/NewInvoice.67c196f0.css","assets/vendor.99304cc5.js","assets/vendor.52e2d7c1.css"])}];let u=k({history:S("/supplier-portal"),routes:J});u.beforeEach((e,o,n)=>h(void 0,null,function*(){let s=i.isLoggedIn;try{yield _.promise}catch(r){s=!1}console.log("isLoggedIn",s),console.log("to",e),console.log("from",o),e.name==="Login"&&s?n({name:"SupplierInvoiceList"}):e.name!=="Login"&&!s?n({name:"Login"}):n()}));const q={};function B(e,o){const n=b("router-view");return C(),E("div",null,[P(n)])}var F=y(q,[["render",B]]);let l=R(F);T("resourceFetcher",O);l.use(u);l.use(w);l.component("Button",$);l.component("Card",U);l.component("Input",j);l.mount("#app");export{f as a,V as b,D as c,u as r,i as s};
|
||||
|
|
@ -0,0 +1 @@
|
|||
var h=(e,s,i)=>new Promise((n,r)=>{var t=c=>{try{l(i.next(c))}catch(g){r(g)}},p=c=>{try{l(i.throw(c))}catch(g){r(g)}},l=c=>c.done?n(c.value):Promise.resolve(c.value).then(t,p);l((i=i.apply(e,s)).next())});import{c as m,r as L,a as k,b as I,d as S,_ as y,e as E,f as P,g as b,o as C,h as R,s as T,i as w,j as $,C as U,I as j,k as O}from"./vendor.3e69a9be.js";const A=function(){const s=document.createElement("link").relList;if(s&&s.supports&&s.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))n(r);new MutationObserver(r=>{for(const t of r)if(t.type==="childList")for(const p of t.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&n(p)}).observe(document,{childList:!0,subtree:!0});function i(r){const t={};return r.integrity&&(t.integrity=r.integrity),r.referrerpolicy&&(t.referrerPolicy=r.referrerpolicy),r.crossorigin==="use-credentials"?t.credentials="include":r.crossorigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function n(r){if(r.ep)return;r.ep=!0;const t=i(r);fetch(r.href,t)}};A();const x="modulepreload",v={},N="/assets/suppliers_portal/supplier-portal/",d=function(s,i){return!i||i.length===0?s():Promise.all(i.map(n=>{if(n=`${N}${n}`,n in v)return;v[n]=!0;const r=n.endsWith(".css"),t=r?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${n}"]${t}`))return;const p=document.createElement("link");if(p.rel=r?"stylesheet":x,r||(p.as="script",p.crossOrigin=""),p.href=n,document.head.appendChild(p),r)return new Promise((l,c)=>{p.addEventListener("load",l),p.addEventListener("error",c)})})).then(()=>s())},_=m({url:"frappe.auth.get_logged_user",cache:"User",onError(e){e&&e.exc_type==="AuthenticationError"&&u.push({name:"LoginPage"})}});function f(){let s=new URLSearchParams(document.cookie.split("; ").join("&")).get("user_id");return s==="Guest"&&(s=null),s}function V(){return new URLSearchParams(document.cookie.split("; ").join("&")).get("supplier_id")}function D(){return new URLSearchParams(document.cookie.split("; ").join("&")).get("supplier_name")}const o=L({supplier_login:m({url:"suppliers_portal.suppliers_portal.api.validate_supplier_id",makeParams({supplier_id:e}){return{supplier_id:e}},onSuccess(e){e&&e.status==="success"&&(_.reload(),o.user=f(),document.cookie=`supplier_id=${e.supplier_id}`,document.cookie=`supplier_name=${e.supplier_name}`,o.supplier_id=e.supplier_id,o.supplier_name=e.supplier_name,o.login.reset(),u.replace({name:"SupplierInvoiceList"}))},onError(e){console.error("Login failed",e)}}),login:m({url:"login",makeParams({email:e,password:s}){return{usr:e,pwd:s}},onSuccess(e){e&&(_.reload(),o.user=f(),document.cookie=`supplier_id=${o.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,document.cookie=`supplier_id=${o.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,o.supplier_id="",o.supplier_name="",o.login.reset(),u.replace({name:"SupplierInvoiceList"}))},onError(e){console.error("Login failed",e)}}),logout:m({url:"logout",onSuccess(){document.cookie=`supplier_id=${o.supplier_id}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,document.cookie=`supplier_id=${o.supplier_name}; expires=Thu, 01 Jan 1970 00:00:00 UTC'`,o.supplier_id="",o.supplier_name="",_.reset(),o.user=f(),u.replace({name:"Login"})}}),user:f(),supplier_id:V(),supplier_name:D(),isLoggedIn:k(()=>!!o.user)}),J=[{name:"Login",path:"/login",component:()=>d(()=>import("./Login.2ab92036.js"),["assets/Login.2ab92036.js","assets/vendor.3e69a9be.js","assets/vendor.43acabc4.css"])},{name:"SupplierInvoiceList",path:"/",component:()=>d(()=>import("./SupplierInvoiceList.d20068c5.js"),["assets/SupplierInvoiceList.d20068c5.js","assets/SupplierInvoiceList.0fe6f3df.css","assets/vendor.3e69a9be.js","assets/vendor.43acabc4.css"])},{name:"SupplierInvoice",path:"/invoices/:supplierInvoiceNumber",component:()=>d(()=>import("./SupplierInvoice.6fa41666.js"),["assets/SupplierInvoice.6fa41666.js","assets/SupplierInvoice.062d709e.css","assets/vendor.3e69a9be.js","assets/vendor.43acabc4.css"]),props:!0},{name:"SupplierInvoiceCreate",path:"/invoices/new",component:()=>d(()=>import("./NewInvoice.15ff173a.js"),["assets/NewInvoice.15ff173a.js","assets/NewInvoice.e56a66d4.css","assets/vendor.3e69a9be.js","assets/vendor.43acabc4.css"])}];let u=I({history:S("/supplier-portal"),routes:J});u.beforeEach((e,s,i)=>h(void 0,null,function*(){let n=o.isLoggedIn;try{yield _.promise}catch(r){n=!1}e.name==="Login"&&n?i({name:"SupplierInvoiceList"}):e.name!=="Login"&&!n?i({name:"Login"}):i()}));const q={};function B(e,s){const i=b("router-view");return C(),E("div",null,[P(i)])}var F=y(q,[["render",B]]);let a=R(F);T("resourceFetcher",O);a.use(u);a.use(w);a.component("Button",$);a.component("Card",U);a.component("Input",j);a.mount("#app");export{f as a,V as b,D as c,u as r,o as s};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -5,9 +5,9 @@
|
|||
<link rel="icon" href="/assets/suppliers_portal/supplier-portal/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Supplier Portal</title>
|
||||
<script type="module" crossorigin src="/assets/suppliers_portal/supplier-portal/assets/index.87704181.js"></script>
|
||||
<link rel="modulepreload" href="/assets/suppliers_portal/supplier-portal/assets/vendor.99304cc5.js">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/vendor.52e2d7c1.css">
|
||||
<script type="module" crossorigin src="/assets/suppliers_portal/supplier-portal/assets/index.a459834f.js"></script>
|
||||
<link rel="modulepreload" href="/assets/suppliers_portal/supplier-portal/assets/vendor.3e69a9be.js">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/vendor.43acabc4.css">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/index.e4753eeb.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ import frappe
|
|||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def validate_supplier_id(supplier_id: str) -> dict:
|
||||
if frappe.db.exists("Supplier", {"name": supplier_id}):
|
||||
user = frappe.get_doc("User", f"{supplier_id}@supplier-portal.com")
|
||||
portal_supplier = frappe.get_doc("Supplier", supplier_id)
|
||||
# login user
|
||||
frappe.local.login_manager.user = user.name
|
||||
frappe.local.login_manager.post_login()
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "User logged in successfully",
|
||||
"supplier_id": supplier_id,
|
||||
"supplier_name": portal_supplier.company_name,
|
||||
}
|
||||
else:
|
||||
frappe.local.response["http_status_code"] = 404
|
||||
frappe.throw("Supplier not found", frappe.AuthenticationError)
|
||||
if frappe.db.exists("Supplier", {"name": supplier_id}):
|
||||
user = frappe.get_doc("User", f"{supplier_id}@supplier-portal.com")
|
||||
portal_supplier = frappe.get_doc("Supplier", supplier_id)
|
||||
# login user
|
||||
frappe.local.login_manager.user = user.name
|
||||
frappe.local.login_manager.post_login()
|
||||
return {
|
||||
"status": "success",
|
||||
"message": "User logged in successfully",
|
||||
"supplier_id": supplier_id,
|
||||
"supplier_name": portal_supplier.company_name,
|
||||
}
|
||||
else:
|
||||
frappe.local.response["http_status_code"] = 404
|
||||
frappe.throw("Supplier not found", frappe.AuthenticationError)
|
||||
|
|
|
|||
137
suppliers_portal/suppliers_portal/custom/purchase_invoice.json
Normal file
137
suppliers_portal/suppliers_portal/custom/purchase_invoice.json
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
{
|
||||
"custom_fields": [
|
||||
{
|
||||
"_assign": null,
|
||||
"_comments": null,
|
||||
"_liked_by": null,
|
||||
"_user_tags": null,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": null,
|
||||
"columns": 0,
|
||||
"creation": "2025-05-19 02:22:24.828657",
|
||||
"default": null,
|
||||
"depends_on": null,
|
||||
"description": null,
|
||||
"docstatus": 0,
|
||||
"dt": "Purchase Invoice",
|
||||
"fetch_from": null,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "service_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
"hide_border": 0,
|
||||
"hide_days": 0,
|
||||
"hide_seconds": 0,
|
||||
"idx": 12,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"insert_after": "due_date",
|
||||
"is_system_generated": 0,
|
||||
"is_virtual": 0,
|
||||
"label": "Service Date",
|
||||
"length": 0,
|
||||
"link_filters": null,
|
||||
"mandatory_depends_on": null,
|
||||
"modified": "2025-05-19 02:22:24.828657",
|
||||
"modified_by": "Administrator",
|
||||
"module": null,
|
||||
"name": "Purchase Invoice-service_date",
|
||||
"no_copy": 0,
|
||||
"non_negative": 0,
|
||||
"options": null,
|
||||
"owner": "Administrator",
|
||||
"permlevel": 0,
|
||||
"placeholder": null,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": null,
|
||||
"read_only": 0,
|
||||
"read_only_depends_on": null,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"show_dashboard": 0,
|
||||
"sort_options": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0,
|
||||
"width": null
|
||||
},
|
||||
{
|
||||
"_assign": null,
|
||||
"_comments": null,
|
||||
"_liked_by": null,
|
||||
"_user_tags": null,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": null,
|
||||
"columns": 0,
|
||||
"creation": "2025-05-19 02:22:24.721092",
|
||||
"default": null,
|
||||
"depends_on": null,
|
||||
"description": null,
|
||||
"docstatus": 0,
|
||||
"dt": "Purchase Invoice",
|
||||
"fetch_from": null,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "site_code",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"hide_border": 0,
|
||||
"hide_days": 0,
|
||||
"hide_seconds": 0,
|
||||
"idx": 6,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"insert_after": "company",
|
||||
"is_system_generated": 0,
|
||||
"is_virtual": 0,
|
||||
"label": "Site Code",
|
||||
"length": 0,
|
||||
"link_filters": null,
|
||||
"mandatory_depends_on": null,
|
||||
"modified": "2025-05-19 02:22:24.721092",
|
||||
"modified_by": "Administrator",
|
||||
"module": null,
|
||||
"name": "Purchase Invoice-site_code",
|
||||
"no_copy": 0,
|
||||
"non_negative": 0,
|
||||
"options": null,
|
||||
"owner": "Administrator",
|
||||
"permlevel": 0,
|
||||
"placeholder": null,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"print_width": null,
|
||||
"read_only": 0,
|
||||
"read_only_depends_on": null,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"show_dashboard": 0,
|
||||
"sort_options": 0,
|
||||
"translatable": 1,
|
||||
"unique": 0,
|
||||
"width": null
|
||||
}
|
||||
],
|
||||
"custom_perms": [],
|
||||
"doctype": "Purchase Invoice",
|
||||
"links": [],
|
||||
"property_setters": [],
|
||||
"sync_on_migrate": 1
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-10-09 16:28:25.879209",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"payment_reference_number",
|
||||
"payment_reference_date",
|
||||
"column_break_nukw",
|
||||
"paid_amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "payment_reference_number",
|
||||
"fieldtype": "Data",
|
||||
"label": "Reference Number"
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_reference_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Payment Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_nukw",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-12 07:16:26.416034",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Invoice Payment References",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) 2024, jeowsome15@gmail.com and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class InvoicePaymentReferences(Document):
|
||||
pass
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// Copyright (c) 2024, jeowsome15@gmail.com and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("Invoices", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:supplier_invoice_number",
|
||||
"creation": "2024-09-24 02:44:02.596278",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"supplier_invoice_number",
|
||||
"supplier",
|
||||
"supplier_name",
|
||||
"column_break_yjtl",
|
||||
"service_date",
|
||||
"invoice_date",
|
||||
"invoice_terms",
|
||||
"due_date",
|
||||
"status",
|
||||
"section_break_swkm",
|
||||
"supplier_invoice_items",
|
||||
"section_break_slsq",
|
||||
"payment_references",
|
||||
"section_break_ajbe",
|
||||
"paid_amount",
|
||||
"amount_due",
|
||||
"total_amount",
|
||||
"column_break_quxe",
|
||||
"section_break_olmx",
|
||||
"service_address",
|
||||
"site_code",
|
||||
"street",
|
||||
"city",
|
||||
"state",
|
||||
"zip_code",
|
||||
"column_break_hvmc",
|
||||
"notes"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"label": "Supplier",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier_invoice_number",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Supplier Invoice Number",
|
||||
"reqd": 1,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"default": "Due On Receipt",
|
||||
"fieldname": "invoice_terms",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Terms",
|
||||
"options": "NET 10\nNET 15\nNET 30\nNET 45\nNET 60\nDue On Receipt",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_yjtl",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_swkm",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier_invoice_items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Invoice Items",
|
||||
"options": "Supplier Invoice Items"
|
||||
},
|
||||
{
|
||||
"default": "Unpaid",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"label": "Status",
|
||||
"options": "Paid\nUnpaid\nOverdue\nPartially Paid\nPartially Paid and Overdue"
|
||||
},
|
||||
{
|
||||
"fieldname": "service_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Service Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_olmx",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "notes",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Notes"
|
||||
},
|
||||
{
|
||||
"fetch_from": "supplier.company_name",
|
||||
"fieldname": "supplier_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Supplier Name",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "service_address",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
"label": "Service Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ajbe",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "total_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Amount",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_quxe",
|
||||
"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": "column_break_hvmc",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Invoice Date",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "due_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Due Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "site_code",
|
||||
"fieldtype": "Data",
|
||||
"label": "Site Code"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_slsq",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "payment_references",
|
||||
"fieldtype": "Table",
|
||||
"label": "Payment References",
|
||||
"options": "Invoice Payment References"
|
||||
},
|
||||
{
|
||||
"fieldname": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Paid Amount",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount_due",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount Due",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-12 08:12:37.273898",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Invoices",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
# Copyright (c) 2024, jeowsome15@gmail.com and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, flt, getdate, nowdate
|
||||
|
||||
|
||||
class Invoices(Document):
|
||||
def validate(self):
|
||||
self.set_due_date()
|
||||
self.set_total_amount()
|
||||
self.set_total_paid()
|
||||
self.update_amount_due()
|
||||
self.update_status()
|
||||
|
||||
def on_update(self):
|
||||
self.update_suppliers_invoice()
|
||||
|
||||
def set_total_amount(self):
|
||||
if self.get("supplier_invoice_items"):
|
||||
total_amount = 0
|
||||
for item in self.get("supplier_invoice_items"):
|
||||
item.amount = flt(item.rate) * flt(item.quantity)
|
||||
total_amount += item.amount
|
||||
self.db_set("total_amount", total_amount)
|
||||
|
||||
def set_due_date(self):
|
||||
if self.invoice_terms:
|
||||
if self.invoice_terms == "NET 10":
|
||||
self.due_date = add_days(self.invoice_date, 10)
|
||||
elif self.invoice_terms == "NET 15":
|
||||
self.due_date = add_days(self.invoice_date, 15)
|
||||
elif self.invoice_terms == "NET 30":
|
||||
self.due_date = add_days(self.invoice_date, 30)
|
||||
elif self.invoice_terms == "NET 45":
|
||||
self.due_date = add_days(self.invoice_date, 45)
|
||||
elif self.invoice_terms == "NET 60":
|
||||
self.due_date = add_days(self.invoice_date, 60)
|
||||
|
||||
def set_total_paid(self):
|
||||
total_paid = 0
|
||||
for item in self.payment_references:
|
||||
total_paid += item.paid_amount
|
||||
self.db_set("paid_amount", total_paid)
|
||||
|
||||
def update_status(self):
|
||||
if self.paid_amount == 0:
|
||||
self.status = "Unpaid"
|
||||
elif self.paid_amount < self.total_amount and self.paid_amount > 0:
|
||||
self.status = "Partially Paid"
|
||||
elif self.paid_amount == self.total_amount:
|
||||
self.status = "Paid"
|
||||
|
||||
if getdate(nowdate()) > getdate(self.due_date):
|
||||
if self.paid_amount == 0:
|
||||
self.status = "Overdue"
|
||||
elif self.paid_amount < self.total_amount:
|
||||
self.status = "Partially Paid and Overdue"
|
||||
|
||||
self.db_set("status", self.status, update_modified=False, commit=True)
|
||||
|
||||
def update_amount_due(self):
|
||||
self.db_set(
|
||||
"amount_due",
|
||||
flt(self.total_amount) - flt(self.paid_amount),
|
||||
update_modified=False,
|
||||
commit=True,
|
||||
)
|
||||
|
||||
def update_suppliers_invoice(self):
|
||||
supplier = frappe.get_doc("Supplier", self.supplier)
|
||||
|
||||
# check if invoice already exists
|
||||
for invoice in supplier.invoices:
|
||||
if invoice.invoice_number == self.name:
|
||||
if invoice.total_due != self.total_amount:
|
||||
invoice.total_amount = self.total_amount
|
||||
if invoice.invoice_date != self.invoice_date:
|
||||
invoice.invoice_date = self.invoice_date
|
||||
if invoice.status != self.status:
|
||||
invoice.status = self.status
|
||||
supplier.save()
|
||||
return
|
||||
|
||||
supplier.append(
|
||||
"invoices",
|
||||
{"invoice_number": self.name},
|
||||
)
|
||||
supplier.save()
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
frappe.listview_settings['Invoices'] = {
|
||||
before_render() {
|
||||
$('button[data-label="Add Invoices"]>span>span').html('Add Invoice')
|
||||
},
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) 2024, jeowsome15@gmail.com and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
|
||||
class TestInvoices(FrappeTestCase):
|
||||
pass
|
||||
|
|
@ -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
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-09-24 02:47:44.354811",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"service_type",
|
||||
"column_break_ocxt",
|
||||
"quantity",
|
||||
"rate",
|
||||
"amount"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "service_type",
|
||||
"fieldtype": "Select",
|
||||
"label": "Service Type",
|
||||
"options": "Repair\nPreventive Maintence"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_ocxt",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "quantity",
|
||||
"fieldtype": "Float",
|
||||
"label": "Quantity"
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate"
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-09-28 00:59:33.037008",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Supplier Invoice Items",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) 2024, jeowsome15@gmail.com and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class SupplierInvoiceItems(Document):
|
||||
pass
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2024-10-09 16:59:58.079331",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"invoice_number",
|
||||
"status",
|
||||
"column_break_mprs",
|
||||
"invoice_date",
|
||||
"terms",
|
||||
"total_due"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "invoice_number",
|
||||
"fieldtype": "Link",
|
||||
"label": "Invoice Number",
|
||||
"options": "Invoices",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "invoice_number.invoice_date",
|
||||
"fieldname": "invoice_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Invoice Date",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "invoice_number.invoice_terms",
|
||||
"fieldname": "terms",
|
||||
"fieldtype": "Data",
|
||||
"label": "Term",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "invoice_number.total_amount",
|
||||
"fieldname": "total_due",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Due",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "invoice_number.status",
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Data",
|
||||
"label": "Status",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_mprs",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-10-09 17:33:59.785852",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Supplier Invoices",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Copyright (c) 2024, jeowsome15@gmail.com and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class SupplierInvoices(Document):
|
||||
pass
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
frappe.query_reports["Supplier Invoices"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "invoice_date_from",
|
||||
"label": __("Invoice Date From"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), // Default to last month
|
||||
"placeholder": __("Invoice Date From")
|
||||
},
|
||||
{
|
||||
"fieldname": "invoice_date_to",
|
||||
"label": __("Invoice Date To"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today(), // Default to today
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"label": __("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier"
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
"label": __("Status"),
|
||||
"fieldtype": "Select",
|
||||
"options": "All\nPaid\nUnpaid\nOverdue",
|
||||
"default": "All",
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
{
|
||||
"add_total_row": 1,
|
||||
"columns": [],
|
||||
"creation": "2024-10-09 15:22:34.256149",
|
||||
"disabled": 1,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"letterhead": null,
|
||||
"modified": "2024-10-09 17:03:58.993246",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Supplier Invoices",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Invoices",
|
||||
"report_name": "Supplier Invoices",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "System Manager"
|
||||
},
|
||||
{
|
||||
"role": "Portal Supplier"
|
||||
}
|
||||
],
|
||||
"timeout": 0
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "invoice_number",
|
||||
"label": "Invoice Number",
|
||||
"fieldtype": "Link",
|
||||
"options": "Invoices",
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier_name",
|
||||
"label": "Supplier Name",
|
||||
"fieldtype": "Data",
|
||||
"width": 200,
|
||||
},
|
||||
{"fieldname": "status", "label": "Status", "fieldtype": "Data", "width": 100},
|
||||
{"fieldname": "invoice_date", "label": "Invoice Date", "fieldtype": "Date"},
|
||||
{"fieldname": "due_date", "label": "Due Date", "fieldtype": "Date"},
|
||||
{"fieldname": "total_amount", "label": "Total Amount", "fieldtype": "Currency"},
|
||||
]
|
||||
|
||||
conditions = ""
|
||||
if filters.get("invoice_date_from"):
|
||||
conditions += " AND invoice_date >= %(invoice_date_from)s"
|
||||
if filters.get("invoice_date_to"):
|
||||
conditions += " AND invoice_date <= %(invoice_date_to)s"
|
||||
if filters.get("supplier"):
|
||||
conditions += " AND supplier = %(supplier)s"
|
||||
if filters.get("status"):
|
||||
if not filters.get("status") == "All":
|
||||
conditions += " AND status = %(status)s"
|
||||
|
||||
query = f"""
|
||||
SELECT
|
||||
name as invoice_number,
|
||||
supplier_name,
|
||||
status,
|
||||
invoice_date,
|
||||
due_date,
|
||||
total_amount
|
||||
FROM
|
||||
`tabInvoices`
|
||||
WHERE
|
||||
docstatus = 0 {conditions}
|
||||
"""
|
||||
data = frappe.db.sql(query, filters, as_dict=True)
|
||||
return columns, data
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
{
|
||||
"app": "supplier_portal",
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"AyiOTTupgl\",\"type\":\"paragraph\",\"data\":{\"text\":\"Documents\",\"col\":12}},{\"id\":\"64VHemT3p8\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Suppliers\",\"col\":4}},{\"id\":\"_vl927eRbo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Invoices\",\"col\":4}},{\"id\":\"j428xL5Lje\",\"type\":\"paragraph\",\"data\":{\"text\":\"Reports\",\"col\":12}},{\"id\":\"CDzGMPClRq\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier Invoices Report\",\"col\":4}}]",
|
||||
"creation": "2024-10-02 17:23:06.804441",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "clipboard",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Supplier Portal",
|
||||
"links": [],
|
||||
"modified": "2024-10-12 07:25:56.798875",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Supplier Portal",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Portal Supplier"
|
||||
},
|
||||
{
|
||||
"role": "Portal User"
|
||||
}
|
||||
],
|
||||
"sequence_id": 2.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Supplier Invoices Report",
|
||||
"link_to": "Supplier Invoices",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Suppliers",
|
||||
"link_to": "Supplier",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Invoices",
|
||||
"link_to": "Invoices",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "Supplier Portal",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
{
|
||||
"app": "Suppliers Portal",
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"Lix3qbsE_H\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\">Suppliers Portal</span>\",\"col\":12}},{\"id\":\"OdX-YH0tro\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"id\":\"_vl927eRbo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Invoices\",\"col\":4}}]",
|
||||
"creation": "2024-10-02 17:23:06.804441",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "clipboard",
|
||||
"idx": 0,
|
||||
"indicator_color": "green",
|
||||
"is_hidden": 0,
|
||||
"label": "Suppliers Portal",
|
||||
"links": [],
|
||||
"modified": "2024-10-03 03:19:57.683322",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Suppliers Portal",
|
||||
"name": "Suppliers Portal",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"roles": [],
|
||||
"sequence_id": 23.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Supplier",
|
||||
"link_to": "Supplier",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Invoices",
|
||||
"link_to": "Invoices",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "Suppliers Portal",
|
||||
"type": "Workspace"
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import frappe
|
||||
|
||||
|
||||
def daily_invoice_status_update():
|
||||
invoices = frappe.get_all(
|
||||
"Invoices",
|
||||
filters={
|
||||
"status": ["in", ["Unpaid", "Partially Paid", "Partially Paid and Overdue"]]
|
||||
},
|
||||
fields=["name"],
|
||||
)
|
||||
for invoice in invoices:
|
||||
invoice_doc = frappe.get_doc("Invoices", invoice.name)
|
||||
invoice_doc.update_status()
|
||||
invoice_doc.save()
|
||||
frappe.db.commit()
|
||||
|
|
@ -5,9 +5,9 @@
|
|||
<link rel="icon" href="/assets/suppliers_portal/supplier-portal/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Supplier Portal</title>
|
||||
<script type="module" crossorigin src="/assets/suppliers_portal/supplier-portal/assets/index.87704181.js"></script>
|
||||
<link rel="modulepreload" href="/assets/suppliers_portal/supplier-portal/assets/vendor.99304cc5.js">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/vendor.52e2d7c1.css">
|
||||
<script type="module" crossorigin src="/assets/suppliers_portal/supplier-portal/assets/index.a459834f.js"></script>
|
||||
<link rel="modulepreload" href="/assets/suppliers_portal/supplier-portal/assets/vendor.3e69a9be.js">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/vendor.43acabc4.css">
|
||||
<link rel="stylesheet" href="/assets/suppliers_portal/supplier-portal/assets/index.e4753eeb.css">
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue