ci: update pre-commit and apply fixes

This commit is contained in:
Rohan Bansal 2025-04-25 16:08:07 +05:30
parent 7b2811e8bd
commit faf0df9879
34 changed files with 1347 additions and 1319 deletions

View file

@ -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
View 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
View 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,
}

View file

@ -1,4 +1,4 @@
{
"semi": false,
"singleQuote": true
"semi": false,
"singleQuote": true
}

View file

@ -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"
}
}

View file

@ -1,6 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -1,5 +1,5 @@
<template>
<div>
<router-view />
</div>
<div>
<router-view />
</div>
</template>

View file

@ -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');
}

View file

@ -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),
})

View file

@ -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' })
}
},
})

View file

@ -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
}
}

View file

@ -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')

View file

@ -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">

View file

@ -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="supplier_invoice_number" 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"
/>
v-model.number="invoiceData.supplier_invoice_number" />
</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,25 +53,20 @@
<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="invoice_date" class="col-sm-3 col-form-label">Invoice Date</label>
<div class="col-sm-8">
<TextInput
:type="'date'"
@ -92,8 +75,7 @@
placeholder="Invoice Date"
id="invoice_date"
:disabled="false"
v-model="invoiceData.invoice_date"
/>
v-model="invoiceData.invoice_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,8 +92,7 @@
class="form-control-plaintext"
id="supplier_name"
:value="invoiceData.supplier_name"
v-if="!isAdministrator"
/>
v-if="!isAdministrator" />
<Autocomplete
id="supplier_name"
:options="portalSuppliers.data"
@ -121,27 +100,20 @@
:hideSearch="!invoiceData.supplier_invoice_number"
: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="invoice_terms" 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.invoice_terms">
<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="invoice_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,21 +205,14 @@
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="notes" class="col-sm-3 col-form-label">Notes</label>
<div class="col-md-8">
<Textarea
:variant="'subtle'"
@ -295,8 +220,7 @@
placeholder="Enter Notes Here"
:disabled="false"
v-model="invoiceData.notes"
id="notes"
/>
id="notes" />
</div>
</div>
</div>
@ -327,13 +251,9 @@
<select
class="form-select form-select-mb h-100 d-inline-block"
aria-label="Service Types"
v-model="item.service_type"
>
v-model="item.service_type">
<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>
@ -344,27 +264,14 @@
</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="total_amount" 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.total_amount" />
</div>
</div>
</div>
@ -492,10 +390,7 @@ const invoiceData = reactive({
const invoiceItems = reactive([])
let invoiceDueDate = computed(() => {
if (invoiceData.invoice_terms) {
let result = addDays(
invoiceData.invoice_date,
parseInvoiceTerms(invoiceData.invoice_terms),
)
let result = addDays(invoiceData.invoice_date, parseInvoiceTerms(invoiceData.invoice_terms))
// return as string
return result.toISOString().split('T')[0]
}
@ -513,7 +408,7 @@ const addRow = () => {
invoiceItems.push(rowObject)
}
const deleteRow = (index) => {
const deleteRow = index => {
invoiceItems.splice(index, 1)
}
@ -535,7 +430,7 @@ const saveInvoice = () => {
state: invoiceData.state,
zip_code: invoiceData.zip_code,
notes: invoiceData.notes,
supplier_invoice_items: invoiceItems.map((item) => {
supplier_invoice_items: invoiceItems.map(item => {
return {
service_type: item.service_type,
quantity: item.quantity,
@ -554,11 +449,11 @@ portalSuppliers.fetch()
// watch for changes in invoiceData.supplier_name
watch(
() => invoiceData.supplier_name,
(newVal) => {
newVal => {
if (newVal) {
invoiceData.supplier = newVal.value
}
},
}
)
</script>

View file

@ -22,35 +22,24 @@
</div>
</div>
</div>
<div
:class="
invoice.doc.supplier_invoice_items
? 'container border-bottom pb-3'
: 'container pb-3'
"
>
<div :class="invoice.doc.supplier_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="invoice.doc.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="invoice.doc.service_date" />
</div>
</div>
</div>
@ -68,25 +56,20 @@
<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="invoice.doc.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="invoice_date" class="col-sm-3 col-form-label">Invoice Date</label>
<div class="col-sm-8">
<TextInput
:type="'date'"
@ -95,8 +78,7 @@
placeholder="Invoice Date"
id="invoice_date"
:disabled="true"
v-model="invoice.doc.invoice_date"
/>
v-model="invoice.doc.invoice_date" />
</div>
</div>
</div>
@ -114,8 +96,7 @@
placeholder="Due Date"
id="due_date"
:disabled="true"
v-model="invoice.doc.due_date"
/>
v-model="invoice.doc.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="invoice_terms" 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"
/>
:value="invoice.doc.invoice_terms" />
</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="invoice.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="invoice.doc.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"
readonly
class="form-control-plaintext"
id="city"
:value="invoice.doc.city"
/>
:value="invoice.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="invoice.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="invoice.doc.zip_code" />
</div>
</div>
</div>
@ -246,21 +204,14 @@
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="notes" class="col-sm-4 col-form-label">Notes</label>
<div class="col-sm-8">
<Textarea
:variant="'subtle'"
@ -269,8 +220,7 @@
:disabled="true"
v-model="invoice.doc.notes"
label=""
id="notes"
/>
id="notes" />
</div>
</div>
</div>
@ -293,10 +243,7 @@
</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 invoice.doc.supplier_invoice_items" :key="item.name">
<th scope="row">{{ index + 1 }}</th>
<td>{{ item.service_type }}</td>
<td>{{ item.quantity }}</td>
@ -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="total_amount" 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="invoice.doc.total_amount" />
</div>
</div>
</div>

View file

@ -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>

View file

@ -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

View file

@ -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: [],
}

View file

@ -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'],
},
})

View file

@ -16,6 +16,6 @@
<div id="popovers"></div>
<script> window.csrf_token = '{{ csrf_token }}'; </script>
</body>
</html>

View file

@ -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)

View file

@ -1,47 +1,42 @@
{
"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": []
}
"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": []
}

View file

@ -1,216 +1,216 @@
{
"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": []
}
"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": []
}

View file

@ -7,84 +7,84 @@ 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 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 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_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_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 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"
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"
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)
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_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)
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
# 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()
supplier.append(
"invoices",
{"invoice_number": self.name},
)
supplier.save()

View file

@ -1,5 +1,5 @@
frappe.listview_settings['Invoices'] = {
before_render() {
$('button[data-label="Add Invoices"]>span>span').html('Add Invoice')
},
}
before_render() {
$('button[data-label="Add Invoices"]>span>span').html('Add Invoice')
},
}

View file

@ -1,56 +1,50 @@
{
"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": []
}
"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": []
}

View file

@ -1,69 +1,62 @@
{
"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": []
}
"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": []
}

View file

@ -1,30 +1,30 @@
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",
}
]
};
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',
},
],
}

View file

@ -1,30 +1,30 @@
{
"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
}
"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
}

View file

@ -2,37 +2,37 @@ 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"},
]
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"
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"""
query = f"""
SELECT
name as invoice_number,
supplier_name,
@ -45,5 +45,5 @@ def execute(filters=None):
WHERE
docstatus = 0 {conditions}
"""
data = frappe.db.sql(query, filters, as_dict=True)
return columns, data
data = frappe.db.sql(query, filters, as_dict=True)
return columns, data

View file

@ -1,63 +1,63 @@
{
"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"
}
"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"
}

View file

@ -1,48 +1,48 @@
{
"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"
}
"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"
}

View file

@ -2,15 +2,13 @@ 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()
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()

View file

@ -16,6 +16,6 @@
<div id="popovers"></div>
<script> window.csrf_token = '{{ csrf_token }}'; </script>
</body>
</html>