Merge branch 'version-13-pre-release' into version-13
This commit is contained in:
commit
a97556fa8d
12
.git-blame-ignore-revs
Normal file
12
.git-blame-ignore-revs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Since version 2.23 (released in August 2019), git-blame has a feature
|
||||||
|
# to ignore or bypass certain commits.
|
||||||
|
#
|
||||||
|
# This file contains a list of commits that are not likely what you
|
||||||
|
# are looking for in a blame, such as mass reformatting or renaming.
|
||||||
|
# You can set this file as a default ignore file for blame by running
|
||||||
|
# the following command.
|
||||||
|
#
|
||||||
|
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||||
|
|
||||||
|
# This commit just changes spaces to tabs for indentation in some files
|
||||||
|
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
73
.github/workflows/patch.yml
vendored
Normal file
73
.github/workflows/patch.yml
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
name: Patch
|
||||||
|
|
||||||
|
on: [pull_request, workflow_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
|
||||||
|
name: Patch Test
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mariadb:10.3
|
||||||
|
env:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: YES
|
||||||
|
ports:
|
||||||
|
- 3306:3306
|
||||||
|
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Setup Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.6
|
||||||
|
|
||||||
|
- name: Add to Hosts
|
||||||
|
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||||
|
|
||||||
|
- name: Cache pip
|
||||||
|
uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-pip-
|
||||||
|
${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Cache node modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
env:
|
||||||
|
cache-name: cache-node-modules
|
||||||
|
with:
|
||||||
|
path: ~/.npm
|
||||||
|
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
|
${{ runner.os }}-build-
|
||||||
|
${{ runner.os }}-
|
||||||
|
|
||||||
|
- name: Get yarn cache directory path
|
||||||
|
id: yarn-cache-dir-path
|
||||||
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
id: yarn-cache
|
||||||
|
with:
|
||||||
|
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||||
|
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-yarn-
|
||||||
|
|
||||||
|
- name: Install
|
||||||
|
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
|
|
||||||
|
- name: Run Patch Tests
|
||||||
|
run: |
|
||||||
|
cd ~/frappe-bench/
|
||||||
|
wget https://erpnext.com/files/v10-erpnext.sql.gz
|
||||||
|
bench --site test_site --force restore ~/frappe-bench/v10-erpnext.sql.gz
|
||||||
|
bench --site test_site migrate
|
@ -1,6 +1,6 @@
|
|||||||
name: CI
|
name: Server
|
||||||
|
|
||||||
on: [pull_request, workflow_dispatch, push]
|
on: [pull_request, workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@ -10,15 +10,9 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
container: [1, 2, 3]
|
||||||
- TYPE: "server"
|
|
||||||
JOB_NAME: "Server"
|
|
||||||
RUN_COMMAND: cd ~/frappe-bench/ && bench --site test_site run-tests --app erpnext --coverage
|
|
||||||
- TYPE: "patch"
|
|
||||||
JOB_NAME: "Patch"
|
|
||||||
RUN_COMMAND: cd ~/frappe-bench/ && wget http://build.erpnext.com/20171108_190013_955977f8_database.sql.gz && bench --site test_site --force restore ~/frappe-bench/20171108_190013_955977f8_database.sql.gz && bench --site test_site migrate
|
|
||||||
|
|
||||||
name: ${{ matrix.JOB_NAME }}
|
name: Python Unit Tests
|
||||||
|
|
||||||
services:
|
services:
|
||||||
mysql:
|
mysql:
|
||||||
@ -36,7 +30,7 @@ jobs:
|
|||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v2
|
||||||
with:
|
with:
|
||||||
python-version: 3.6
|
python-version: 3.7
|
||||||
|
|
||||||
- name: Add to Hosts
|
- name: Add to Hosts
|
||||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||||
@ -49,6 +43,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-pip-
|
${{ runner.os }}-pip-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
|
|
||||||
- name: Cache node modules
|
- name: Cache node modules
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
env:
|
env:
|
||||||
@ -60,6 +55,7 @@ jobs:
|
|||||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||||
${{ runner.os }}-build-
|
${{ runner.os }}-build-
|
||||||
${{ runner.os }}-
|
${{ runner.os }}-
|
||||||
|
|
||||||
- name: Get yarn cache directory path
|
- name: Get yarn cache directory path
|
||||||
id: yarn-cache-dir-path
|
id: yarn-cache-dir-path
|
||||||
run: echo "::set-output name=dir::$(yarn cache dir)"
|
run: echo "::set-output name=dir::$(yarn cache dir)"
|
||||||
@ -76,33 +72,39 @@ jobs:
|
|||||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: ${{ matrix.RUN_COMMAND }}
|
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage
|
||||||
env:
|
env:
|
||||||
TYPE: ${{ matrix.TYPE }}
|
TYPE: server
|
||||||
|
CI_BUILD_ID: ${{ github.run_id }}
|
||||||
|
ORCHESTRATOR_URL: http://test-orchestrator.frappe.io
|
||||||
|
|
||||||
- name: Coverage - Pull Request
|
- name: Upload Coverage Data
|
||||||
if: matrix.TYPE == 'server' && github.event_name == 'pull_request'
|
|
||||||
run: |
|
run: |
|
||||||
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
|
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
|
||||||
cd ${GITHUB_WORKSPACE}
|
cd ${GITHUB_WORKSPACE}
|
||||||
pip install coveralls==2.2.0
|
pip3 install coverage==5.5
|
||||||
pip install coverage==4.5.4
|
pip3 install coveralls==3.0.1
|
||||||
coveralls --service=github
|
coveralls
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
|
COVERALLS_FLAG_NAME: run-${{ matrix.container }}
|
||||||
COVERALLS_SERVICE_NAME: github
|
COVERALLS_SERVICE_NAME: ${{ github.event_name == 'pull_request' && 'github' || 'github-actions' }}
|
||||||
|
COVERALLS_PARALLEL: true
|
||||||
|
|
||||||
- name: Coverage - Push
|
coveralls:
|
||||||
if: matrix.TYPE == 'server' && github.event_name == 'push'
|
name: Coverage Wrap Up
|
||||||
|
needs: test
|
||||||
|
container: python:3-slim
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- name: Clone
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Coveralls Finished
|
||||||
run: |
|
run: |
|
||||||
cp ~/frappe-bench/sites/.coverage ${GITHUB_WORKSPACE}
|
|
||||||
cd ${GITHUB_WORKSPACE}
|
cd ${GITHUB_WORKSPACE}
|
||||||
pip install coveralls==2.2.0
|
pip3 install coverage==5.5
|
||||||
pip install coverage==4.5.4
|
pip3 install coveralls==3.0.1
|
||||||
coveralls --service=github-actions
|
coveralls --finish
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
|
|
||||||
COVERALLS_SERVICE_NAME: github-actions
|
|
||||||
|
|
@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '13.4.1'
|
__version__ = '13.5.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
@ -27,7 +27,7 @@ class AccountingDimension(Document):
|
|||||||
exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
|
exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
|
||||||
|
|
||||||
if exists and self.is_new():
|
if exists and self.is_new():
|
||||||
frappe.throw("Document Type already used as a dimension")
|
frappe.throw(_("Document Type already used as a dimension"))
|
||||||
|
|
||||||
if not self.is_new():
|
if not self.is_new():
|
||||||
self.validate_document_type_change()
|
self.validate_document_type_change()
|
||||||
|
@ -7,7 +7,8 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import delete_accounting_dimension
|
|
||||||
|
test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
|
||||||
|
|
||||||
class TestAccountingDimension(unittest.TestCase):
|
class TestAccountingDimension(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -9,6 +9,8 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
|
|||||||
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension
|
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import create_dimension, disable_dimension
|
||||||
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
|
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
|
||||||
|
|
||||||
|
test_dependencies = ['Location', 'Cost Center', 'Department']
|
||||||
|
|
||||||
class TestAccountingDimensionFilter(unittest.TestCase):
|
class TestAccountingDimensionFilter(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
create_dimension()
|
create_dimension()
|
||||||
|
@ -10,6 +10,8 @@ from erpnext.accounts.general_ledger import ClosedAccountingPeriod
|
|||||||
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
|
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
|
||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
|
test_dependencies = ['Item']
|
||||||
|
|
||||||
class TestAccountingPeriod(unittest.TestCase):
|
class TestAccountingPeriod(unittest.TestCase):
|
||||||
def test_overlap(self):
|
def test_overlap(self):
|
||||||
ap1 = create_accounting_period(start_date = "2018-04-01",
|
ap1 = create_accounting_period(start_date = "2018-04-01",
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
"delete_linked_ledger_entries",
|
"delete_linked_ledger_entries",
|
||||||
"book_asset_depreciation_entry_automatically",
|
"book_asset_depreciation_entry_automatically",
|
||||||
"unlink_advance_payment_on_cancelation_of_order",
|
"unlink_advance_payment_on_cancelation_of_order",
|
||||||
|
"post_change_gl_entries",
|
||||||
"tax_settings_section",
|
"tax_settings_section",
|
||||||
"determine_address_tax_category_from",
|
"determine_address_tax_category_from",
|
||||||
"column_break_19",
|
"column_break_19",
|
||||||
@ -253,6 +254,12 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_19",
|
"fieldname": "column_break_19",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "post_change_gl_entries",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Post Ledger Entries for Given Change"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
@ -260,7 +267,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-04-30 15:25:10.381008",
|
"modified": "2021-05-25 12:34:05.858669",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
from frappe import _
|
||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||||
@ -24,7 +25,7 @@ class AccountsSettings(Document):
|
|||||||
def validate_stale_days(self):
|
def validate_stale_days(self):
|
||||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
"Stale Days should start from 1.", title='Error', indicator='red',
|
_("Stale Days should start from 1."), title='Error', indicator='red',
|
||||||
raise_exception=1)
|
raise_exception=1)
|
||||||
|
|
||||||
def enable_payment_schedule_in_print(self):
|
def enable_payment_schedule_in_print(self):
|
||||||
|
@ -0,0 +1,197 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2020-09-12 22:26:19.594367",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "Setup",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"add_deduct_tax",
|
||||||
|
"charge_type",
|
||||||
|
"row_id",
|
||||||
|
"account_head",
|
||||||
|
"col_break_1",
|
||||||
|
"description",
|
||||||
|
"included_in_paid_amount",
|
||||||
|
"accounting_dimensions_section",
|
||||||
|
"cost_center",
|
||||||
|
"dimension_col_break",
|
||||||
|
"section_break_8",
|
||||||
|
"rate",
|
||||||
|
"section_break_9",
|
||||||
|
"currency",
|
||||||
|
"tax_amount",
|
||||||
|
"total",
|
||||||
|
"allocated_amount",
|
||||||
|
"column_break_13",
|
||||||
|
"base_tax_amount",
|
||||||
|
"base_total",
|
||||||
|
"base_allocated_amount"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "charge_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Type",
|
||||||
|
"oldfieldname": "charge_type",
|
||||||
|
"oldfieldtype": "Select",
|
||||||
|
"options": "\nActual\nOn Paid Amount\nOn Previous Row Amount\nOn Previous Row Total",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:[\"On Previous Row Amount\", \"On Previous Row Total\"].indexOf(doc.charge_type)!==-1",
|
||||||
|
"fieldname": "row_id",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"label": "Reference Row #",
|
||||||
|
"oldfieldname": "row_id",
|
||||||
|
"oldfieldtype": "Data"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "account_head",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Account Head",
|
||||||
|
"oldfieldname": "account_head",
|
||||||
|
"oldfieldtype": "Link",
|
||||||
|
"options": "Account",
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "col_break_1",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"width": "50%"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Description",
|
||||||
|
"oldfieldname": "description",
|
||||||
|
"oldfieldtype": "Small Text",
|
||||||
|
"print_width": "300px",
|
||||||
|
"reqd": 1,
|
||||||
|
"width": "300px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "accounting_dimensions_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Accounting Dimensions"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": ":Company",
|
||||||
|
"fieldname": "cost_center",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Cost Center",
|
||||||
|
"oldfieldname": "cost_center_other_charges",
|
||||||
|
"oldfieldtype": "Link",
|
||||||
|
"options": "Cost Center"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "dimension_col_break",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_8",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Rate",
|
||||||
|
"oldfieldname": "rate",
|
||||||
|
"oldfieldtype": "Currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_9",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "tax_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Amount",
|
||||||
|
"options": "currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Total",
|
||||||
|
"options": "currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_13",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_tax_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Amount (Company Currency)",
|
||||||
|
"oldfieldname": "tax_amount",
|
||||||
|
"oldfieldtype": "Currency",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total (Company Currency)",
|
||||||
|
"oldfieldname": "total",
|
||||||
|
"oldfieldtype": "Currency",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "add_deduct_tax",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Add Or Deduct",
|
||||||
|
"options": "Add\nDeduct",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "included_in_paid_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Considered In Paid Amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "allocated_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Allocated Amount",
|
||||||
|
"options": "currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_allocated_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Allocated Amount (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "account_head.account_currency",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Account Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2021-06-09 11:46:58.373170",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Advance Taxes and Charges",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "ASC"
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class AdvanceTaxesandCharges(Document):
|
||||||
|
pass
|
@ -11,6 +11,8 @@ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_pur
|
|||||||
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
|
from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError
|
||||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
|
|
||||||
|
test_dependencies = ['Monthly Distribution']
|
||||||
|
|
||||||
class TestBudget(unittest.TestCase):
|
class TestBudget(unittest.TestCase):
|
||||||
def test_monthly_budget_crossed_ignore(self):
|
def test_monthly_budget_crossed_ignore(self):
|
||||||
set_total_expense_zero(nowdate(), "cost_center")
|
set_total_expense_zero(nowdate(), "cost_center")
|
||||||
|
@ -22,7 +22,7 @@ def validate_company(company):
|
|||||||
'allow_account_creation_against_child_company'])
|
'allow_account_creation_against_child_company'])
|
||||||
|
|
||||||
if parent_company and (not allow_account_creation_against_child_company):
|
if parent_company and (not allow_account_creation_against_child_company):
|
||||||
msg = _("{} is a child company. ").format(frappe.bold(company))
|
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
|
||||||
msg += _("Please import accounts against parent company or enable {} in company master.").format(
|
msg += _("Please import accounts against parent company or enable {} in company master.").format(
|
||||||
frappe.bold('Allow Account Creation Against Child Company'))
|
frappe.bold('Allow Account Creation Against Child Company'))
|
||||||
frappe.throw(msg, title=_('Wrong Company'))
|
frappe.throw(msg, title=_('Wrong Company'))
|
||||||
@ -56,7 +56,7 @@ def get_file(file_name):
|
|||||||
extension = extension.lstrip(".")
|
extension = extension.lstrip(".")
|
||||||
|
|
||||||
if extension not in ('csv', 'xlsx', 'xls'):
|
if extension not in ('csv', 'xlsx', 'xls'):
|
||||||
frappe.throw("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload")
|
frappe.throw(_("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"))
|
||||||
|
|
||||||
return file_doc, extension
|
return file_doc, extension
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ def validate_accounts(file_name):
|
|||||||
accounts_dict = {}
|
accounts_dict = {}
|
||||||
for account in accounts:
|
for account in accounts:
|
||||||
accounts_dict.setdefault(account["account_name"], account)
|
accounts_dict.setdefault(account["account_name"], account)
|
||||||
if not hasattr(account, "parent_account"):
|
if "parent_account" not in account:
|
||||||
msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
|
msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
|
||||||
msg += "<br><br>"
|
msg += "<br><br>"
|
||||||
msg += _("Alternatively, you can download the template and fill your data in.")
|
msg += _("Alternatively, you can download the template and fill your data in.")
|
||||||
|
@ -49,11 +49,11 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_status()
|
self.set_status(cancel=1)
|
||||||
self.update_sales_invoice()
|
self.update_sales_invoice()
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
def set_status(self, status=None):
|
def set_status(self, status=None, cancel=0):
|
||||||
if status:
|
if status:
|
||||||
self.status = status
|
self.status = status
|
||||||
self.db_set("status", status)
|
self.db_set("status", status)
|
||||||
@ -66,6 +66,9 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
elif self.docstatus == 2:
|
elif self.docstatus == 2:
|
||||||
self.status = "Cancelled"
|
self.status = "Cancelled"
|
||||||
|
|
||||||
|
if cancel:
|
||||||
|
self.db_set('status', self.status, update_modified = True)
|
||||||
|
|
||||||
def update_sales_invoice(self):
|
def update_sales_invoice(self):
|
||||||
for d in self.invoices:
|
for d in self.invoices:
|
||||||
if self.docstatus == 1:
|
if self.docstatus == 1:
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
{% include "erpnext/public/js/controllers/accounts.js" %}
|
{% include "erpnext/public/js/controllers/accounts.js" %}
|
||||||
frappe.provide("erpnext.accounts.dimensions");
|
frappe.provide("erpnext.accounts.dimensions");
|
||||||
|
|
||||||
|
cur_frm.cscript.tax_table = "Advance Taxes and Charges";
|
||||||
|
|
||||||
frappe.ui.form.on('Payment Entry', {
|
frappe.ui.form.on('Payment Entry', {
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
if(frm.doc.__islocal) {
|
if(frm.doc.__islocal) {
|
||||||
@ -91,6 +93,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("advance_tax_account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"company": frm.doc.company,
|
||||||
|
"root_type": ["in", ["Asset", "Liability"]],
|
||||||
|
"is_group": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
frm.set_query("reference_doctype", "references", function() {
|
frm.set_query("reference_doctype", "references", function() {
|
||||||
if (frm.doc.party_type == "Customer") {
|
if (frm.doc.party_type == "Customer") {
|
||||||
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
|
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"];
|
||||||
@ -182,6 +194,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
|
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
|
||||||
|
|
||||||
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
|
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
|
||||||
|
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
|
||||||
|
(frm.doc.paid_from_account_currency != company_currency));
|
||||||
|
|
||||||
frm.toggle_display("base_received_amount", (
|
frm.toggle_display("base_received_amount", (
|
||||||
frm.doc.paid_to_account_currency != company_currency
|
frm.doc.paid_to_account_currency != company_currency
|
||||||
@ -216,7 +230,7 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
|
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
|
||||||
|
|
||||||
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
|
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
|
||||||
"difference_amount"], company_currency);
|
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency);
|
||||||
|
|
||||||
frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
|
frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
|
||||||
frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);
|
frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);
|
||||||
@ -224,11 +238,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
var party_account_currency = frm.doc.payment_type=="Receive" ?
|
var party_account_currency = frm.doc.payment_type=="Receive" ?
|
||||||
frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
|
frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency;
|
||||||
|
|
||||||
frm.set_currency_labels(["total_allocated_amount", "unallocated_amount"], party_account_currency);
|
frm.set_currency_labels(["total_allocated_amount", "unallocated_amount",
|
||||||
|
"total_taxes_and_charges"], party_account_currency);
|
||||||
|
|
||||||
var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency"
|
var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency"
|
||||||
frm.set_df_property("total_allocated_amount", "options", currency_field);
|
frm.set_df_property("total_allocated_amount", "options", currency_field);
|
||||||
frm.set_df_property("unallocated_amount", "options", currency_field);
|
frm.set_df_property("unallocated_amount", "options", currency_field);
|
||||||
|
frm.set_df_property("total_taxes_and_charges", "options", currency_field);
|
||||||
frm.set_df_property("party_balance", "options", currency_field);
|
frm.set_df_property("party_balance", "options", currency_field);
|
||||||
|
|
||||||
frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"],
|
frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"],
|
||||||
@ -364,6 +380,16 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
apply_tax_withholding_amount: function(frm) {
|
||||||
|
if (!frm.doc.apply_tax_withholding_amount) {
|
||||||
|
frm.set_value("tax_withholding_category", '');
|
||||||
|
} else {
|
||||||
|
frappe.db.get_value('Supplier', frm.doc.party, 'tax_withholding_category', (values) => {
|
||||||
|
frm.set_value("tax_withholding_category", values.tax_withholding_category);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
paid_from: function(frm) {
|
paid_from: function(frm) {
|
||||||
if(frm.set_party_account_based_on_party) return;
|
if(frm.set_party_account_based_on_party) return;
|
||||||
|
|
||||||
@ -843,12 +869,12 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
if(frm.doc.payment_type == "Receive"
|
if(frm.doc.payment_type == "Receive"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
||||||
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
|
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
|
||||||
unallocated_amount = (frm.doc.base_received_amount + total_deductions
|
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
|
||||||
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
+ frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
||||||
} else if (frm.doc.payment_type == "Pay"
|
} else if (frm.doc.payment_type == "Pay"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
||||||
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
|
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
|
||||||
unallocated_amount = (frm.doc.base_paid_amount - (total_deductions
|
unallocated_amount = (frm.doc.base_paid_amount + frm.doc.base_total_taxes_and_charges - (total_deductions
|
||||||
+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
|
+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -874,7 +900,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||||
function(d) { return flt(d.amount) }));
|
function(d) { return flt(d.amount) }));
|
||||||
|
|
||||||
frm.set_value("difference_amount", difference_amount - total_deductions);
|
frm.set_value("difference_amount", difference_amount - total_deductions +
|
||||||
|
frm.doc.base_total_taxes_and_charges);
|
||||||
|
|
||||||
frm.events.hide_unhide_fields(frm);
|
frm.events.hide_unhide_fields(frm);
|
||||||
},
|
},
|
||||||
@ -1002,7 +1029,266 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
sales_taxes_and_charges_template: function(frm) {
|
||||||
|
frm.trigger('fetch_taxes_from_template');
|
||||||
|
},
|
||||||
|
|
||||||
|
purchase_taxes_and_charges_template: function(frm) {
|
||||||
|
frm.trigger('fetch_taxes_from_template');
|
||||||
|
},
|
||||||
|
|
||||||
|
fetch_taxes_from_template: function(frm) {
|
||||||
|
let master_doctype = '';
|
||||||
|
let taxes_and_charges = '';
|
||||||
|
|
||||||
|
if (frm.doc.party_type == 'Supplier') {
|
||||||
|
master_doctype = 'Purchase Taxes and Charges Template';
|
||||||
|
taxes_and_charges = frm.doc.purchase_taxes_and_charges_template;
|
||||||
|
} else if (frm.doc.party_type == 'Customer') {
|
||||||
|
master_doctype = 'Sales Taxes and Charges Template';
|
||||||
|
taxes_and_charges = frm.doc.sales_taxes_and_charges_template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!taxes_and_charges) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.controllers.accounts_controller.get_taxes_and_charges",
|
||||||
|
args: {
|
||||||
|
"master_doctype": master_doctype,
|
||||||
|
"master_name": taxes_and_charges
|
||||||
|
},
|
||||||
|
callback: function(r) {
|
||||||
|
if(!r.exc && r.message) {
|
||||||
|
// set taxes table
|
||||||
|
if(r.message) {
|
||||||
|
for (let tax of r.message) {
|
||||||
|
if (tax.charge_type === 'On Net Total') {
|
||||||
|
tax.charge_type = 'On Paid Amount';
|
||||||
|
}
|
||||||
|
me.frm.add_child("taxes", tax);
|
||||||
|
}
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
apply_taxes: function(frm) {
|
||||||
|
frm.events.initialize_taxes(frm);
|
||||||
|
frm.events.determine_exclusive_rate(frm);
|
||||||
|
frm.events.calculate_taxes(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize_taxes: function(frm) {
|
||||||
|
$.each(frm.doc["taxes"] || [], function(i, tax) {
|
||||||
|
frm.events.validate_taxes_and_charges(tax);
|
||||||
|
frm.events.validate_inclusive_tax(tax);
|
||||||
|
tax.item_wise_tax_detail = {};
|
||||||
|
let tax_fields = ["total", "tax_fraction_for_current_item",
|
||||||
|
"grand_total_fraction_for_current_item"];
|
||||||
|
|
||||||
|
if (cstr(tax.charge_type) != "Actual") {
|
||||||
|
tax_fields.push("tax_amount");
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
|
||||||
|
|
||||||
|
frm.doc.paid_amount_after_tax = frm.doc.paid_amount;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
validate_taxes_and_charges: function(d) {
|
||||||
|
let msg = "";
|
||||||
|
|
||||||
|
if (d.account_head && !d.description) {
|
||||||
|
// set description from account head
|
||||||
|
d.description = d.account_head.split(' - ').slice(0, -1).join(' - ');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!d.charge_type && (d.row_id || d.rate || d.tax_amount)) {
|
||||||
|
msg = __("Please select Charge Type first");
|
||||||
|
d.row_id = "";
|
||||||
|
d.rate = d.tax_amount = 0.0;
|
||||||
|
} else if ((d.charge_type == 'Actual' || d.charge_type == 'On Net Total' || d.charge_type == 'On Paid Amount') && d.row_id) {
|
||||||
|
msg = __("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'");
|
||||||
|
d.row_id = "";
|
||||||
|
} else if ((d.charge_type == 'On Previous Row Amount' || d.charge_type == 'On Previous Row Total') && d.row_id) {
|
||||||
|
if (d.idx == 1) {
|
||||||
|
msg = __("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row");
|
||||||
|
d.charge_type = '';
|
||||||
|
} else if (!d.row_id) {
|
||||||
|
msg = __("Please specify a valid Row ID for row {0} in table {1}", [d.idx, __(d.doctype)]);
|
||||||
|
d.row_id = "";
|
||||||
|
} else if (d.row_id && d.row_id >= d.idx) {
|
||||||
|
msg = __("Cannot refer row number greater than or equal to current row number for this Charge type");
|
||||||
|
d.row_id = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (msg) {
|
||||||
|
frappe.validated = false;
|
||||||
|
refresh_field("taxes");
|
||||||
|
frappe.throw(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
validate_inclusive_tax: function(tax) {
|
||||||
|
let actual_type_error = function() {
|
||||||
|
let msg = __("Actual type tax cannot be included in Item rate in row {0}", [tax.idx])
|
||||||
|
frappe.throw(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_previous_row_error = function(row_range) {
|
||||||
|
let msg = __("For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included",
|
||||||
|
[tax.idx, __(tax.doctype), tax.charge_type, row_range])
|
||||||
|
frappe.throw(msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(cint(tax.included_in_paid_amount)) {
|
||||||
|
if(tax.charge_type == "Actual") {
|
||||||
|
// inclusive tax cannot be of type Actual
|
||||||
|
actual_type_error();
|
||||||
|
} else if(tax.charge_type == "On Previous Row Amount" &&
|
||||||
|
!cint(this.frm.doc["taxes"][tax.row_id - 1].included_in_paid_amount)
|
||||||
|
) {
|
||||||
|
// referred row should also be an inclusive tax
|
||||||
|
on_previous_row_error(tax.row_id);
|
||||||
|
} else if(tax.charge_type == "On Previous Row Total") {
|
||||||
|
let taxes_not_included = $.map(this.frm.doc["taxes"].slice(0, tax.row_id),
|
||||||
|
function(t) { return cint(t.included_in_paid_amount) ? null : t; });
|
||||||
|
if(taxes_not_included.length > 0) {
|
||||||
|
// all rows above this tax should be inclusive
|
||||||
|
on_previous_row_error(tax.row_id == 1 ? "1" : "1 - " + tax.row_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
determine_exclusive_rate: function(frm) {
|
||||||
|
let has_inclusive_tax = false;
|
||||||
|
$.each(frm.doc["taxes"] || [], function(i, row) {
|
||||||
|
if(cint(row.included_in_paid_amount)) has_inclusive_tax = true;
|
||||||
|
});
|
||||||
|
if(has_inclusive_tax==false) return;
|
||||||
|
|
||||||
|
let cumulated_tax_fraction = 0.0;
|
||||||
|
$.each(frm.doc["taxes"] || [], function(i, tax) {
|
||||||
|
tax.tax_fraction_for_current_item = frm.events.get_current_tax_fraction(frm, tax);
|
||||||
|
|
||||||
|
if(i==0) {
|
||||||
|
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
|
||||||
|
} else {
|
||||||
|
tax.grand_total_fraction_for_current_item =
|
||||||
|
me.frm.doc["taxes"][i-1].grand_total_fraction_for_current_item +
|
||||||
|
tax.tax_fraction_for_current_item;
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
|
||||||
|
frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction))
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get_current_tax_fraction: function(frm, tax) {
|
||||||
|
let current_tax_fraction = 0.0;
|
||||||
|
|
||||||
|
if(cint(tax.included_in_paid_amount)) {
|
||||||
|
let tax_rate = tax.rate;
|
||||||
|
|
||||||
|
if(tax.charge_type == "On Paid Amount") {
|
||||||
|
current_tax_fraction = (tax_rate / 100.0);
|
||||||
|
} else if(tax.charge_type == "On Previous Row Amount") {
|
||||||
|
current_tax_fraction = (tax_rate / 100.0) *
|
||||||
|
frm.doc["taxes"][cint(tax.row_id) - 1].tax_fraction_for_current_item;
|
||||||
|
} else if(tax.charge_type == "On Previous Row Total") {
|
||||||
|
current_tax_fraction = (tax_rate / 100.0) *
|
||||||
|
frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
|
||||||
|
current_tax_fraction *= -1;
|
||||||
|
}
|
||||||
|
return current_tax_fraction;
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
calculate_taxes: function(frm) {
|
||||||
|
frm.doc.total_taxes_and_charges = 0.0;
|
||||||
|
frm.doc.base_total_taxes_and_charges = 0.0;
|
||||||
|
|
||||||
|
let actual_tax_dict = {};
|
||||||
|
|
||||||
|
// maintain actual tax rate based on idx
|
||||||
|
$.each(frm.doc["taxes"] || [], function(i, tax) {
|
||||||
|
if (tax.charge_type == "Actual") {
|
||||||
|
actual_tax_dict[tax.idx] = flt(tax.tax_amount, precision("tax_amount", tax));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
|
||||||
|
let current_tax_amount = frm.events.get_current_tax_amount(frm, tax);
|
||||||
|
|
||||||
|
// Adjust divisional loss to the last item
|
||||||
|
if (tax.charge_type == "Actual") {
|
||||||
|
actual_tax_dict[tax.idx] -= current_tax_amount;
|
||||||
|
if (i == frm.doc["taxes"].length - 1) {
|
||||||
|
current_tax_amount += actual_tax_dict[tax.idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tax.tax_amount = current_tax_amount;
|
||||||
|
tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate;
|
||||||
|
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||||
|
|
||||||
|
if(i==0) {
|
||||||
|
tax.total = flt(frm.doc.paid_amount_after_tax + current_tax_amount, precision("total", tax));
|
||||||
|
} else {
|
||||||
|
tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
|
||||||
|
}
|
||||||
|
|
||||||
|
tax.base_total = tax.total * frm.doc.source_exchange_rate;
|
||||||
|
frm.doc.total_taxes_and_charges += current_tax_amount;
|
||||||
|
frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate;
|
||||||
|
|
||||||
|
frm.refresh_field('taxes');
|
||||||
|
frm.refresh_field('total_taxes_and_charges');
|
||||||
|
frm.refresh_field('base_total_taxes_and_charges');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
get_current_tax_amount: function(frm, tax) {
|
||||||
|
let tax_rate = tax.rate;
|
||||||
|
let current_tax_amount = 0.0;
|
||||||
|
|
||||||
|
// To set row_id by default as previous row.
|
||||||
|
if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
|
||||||
|
if (tax.idx === 1) {
|
||||||
|
frappe.throw(
|
||||||
|
__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tax.charge_type == "Actual") {
|
||||||
|
current_tax_amount = flt(tax.tax_amount, precision("tax_amount", tax))
|
||||||
|
} else if(tax.charge_type == "On Paid Amount") {
|
||||||
|
current_tax_amount = flt((tax_rate / 100.0) * frm.doc.paid_amount_after_tax);
|
||||||
|
} else if(tax.charge_type == "On Previous Row Amount") {
|
||||||
|
current_tax_amount = flt((tax_rate / 100.0) *
|
||||||
|
frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount);
|
||||||
|
|
||||||
|
} else if(tax.charge_type == "On Previous Row Total") {
|
||||||
|
current_tax_amount = flt((tax_rate / 100.0) *
|
||||||
|
frm.doc["taxes"][cint(tax.row_id) - 1].total);
|
||||||
|
}
|
||||||
|
|
||||||
|
return current_tax_amount;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -1049,6 +1335,38 @@ frappe.ui.form.on('Payment Entry Reference', {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
frappe.ui.form.on('Advance Taxes and Charges', {
|
||||||
|
rate: function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
tax_amount : function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
row_id: function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
taxes_remove: function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
included_in_paid_amount: function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
},
|
||||||
|
|
||||||
|
charge_type: function(frm) {
|
||||||
|
frm.events.apply_taxes(frm);
|
||||||
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
frappe.ui.form.on('Payment Entry Deduction', {
|
frappe.ui.form.on('Payment Entry Deduction', {
|
||||||
amount: function(frm) {
|
amount: function(frm) {
|
||||||
frm.events.set_unallocated_amount(frm);
|
frm.events.set_unallocated_amount(frm);
|
||||||
|
@ -35,12 +35,16 @@
|
|||||||
"paid_to_account_balance",
|
"paid_to_account_balance",
|
||||||
"payment_amounts_section",
|
"payment_amounts_section",
|
||||||
"paid_amount",
|
"paid_amount",
|
||||||
|
"paid_amount_after_tax",
|
||||||
"source_exchange_rate",
|
"source_exchange_rate",
|
||||||
"base_paid_amount",
|
"base_paid_amount",
|
||||||
|
"base_paid_amount_after_tax",
|
||||||
"column_break_21",
|
"column_break_21",
|
||||||
"received_amount",
|
"received_amount",
|
||||||
|
"received_amount_after_tax",
|
||||||
"target_exchange_rate",
|
"target_exchange_rate",
|
||||||
"base_received_amount",
|
"base_received_amount",
|
||||||
|
"base_received_amount_after_tax",
|
||||||
"section_break_14",
|
"section_break_14",
|
||||||
"get_outstanding_invoice",
|
"get_outstanding_invoice",
|
||||||
"references",
|
"references",
|
||||||
@ -52,6 +56,17 @@
|
|||||||
"unallocated_amount",
|
"unallocated_amount",
|
||||||
"difference_amount",
|
"difference_amount",
|
||||||
"write_off_difference_amount",
|
"write_off_difference_amount",
|
||||||
|
"taxes_and_charges_section",
|
||||||
|
"purchase_taxes_and_charges_template",
|
||||||
|
"sales_taxes_and_charges_template",
|
||||||
|
"advance_tax_account",
|
||||||
|
"column_break_55",
|
||||||
|
"apply_tax_withholding_amount",
|
||||||
|
"tax_withholding_category",
|
||||||
|
"section_break_56",
|
||||||
|
"taxes",
|
||||||
|
"base_total_taxes_and_charges",
|
||||||
|
"total_taxes_and_charges",
|
||||||
"deductions_or_loss_section",
|
"deductions_or_loss_section",
|
||||||
"deductions",
|
"deductions",
|
||||||
"transaction_references",
|
"transaction_references",
|
||||||
@ -320,6 +335,7 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "doc.received_amount",
|
||||||
"fieldname": "base_received_amount",
|
"fieldname": "base_received_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Received Amount (Company Currency)",
|
"label": "Received Amount (Company Currency)",
|
||||||
@ -584,12 +600,114 @@
|
|||||||
"fieldname": "custom_remarks",
|
"fieldname": "custom_remarks",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Custom Remarks"
|
"label": "Custom Remarks"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.apply_tax_withholding_amount",
|
||||||
|
"fieldname": "tax_withholding_category",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Tax Withholding Category",
|
||||||
|
"mandatory_depends_on": "eval:doc.apply_tax_withholding_amount",
|
||||||
|
"options": "Tax Withholding Category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.party_type == 'Supplier'",
|
||||||
|
"fieldname": "apply_tax_withholding_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Apply Tax Withholding Amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "taxes_and_charges_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Taxes and Charges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.party_type == 'Supplier'",
|
||||||
|
"fieldname": "purchase_taxes_and_charges_template",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Taxes and Charges Template",
|
||||||
|
"options": "Purchase Taxes and Charges Template"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.party_type == 'Customer'",
|
||||||
|
"fieldname": "sales_taxes_and_charges_template",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Taxes and Charges Template",
|
||||||
|
"options": "Sales Taxes and Charges Template"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval: doc.party_type == 'Supplier' || doc.party_type == 'Customer'",
|
||||||
|
"fieldname": "taxes",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Advance Taxes and Charges",
|
||||||
|
"options": "Advance Taxes and Charges"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_total_taxes_and_charges",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Taxes and Charges (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_taxes_and_charges",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Total Taxes and Charges",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "paid_amount_after_tax",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Paid Amount After Tax",
|
||||||
|
"options": "paid_from_account_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "base_paid_amount_after_tax",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Paid Amount After Tax (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_55",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_56",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.apply_tax_withholding_amount",
|
||||||
|
"description": "Provisional tax account for advance tax. Taxes are parked in this account until payments are allocated to invoices",
|
||||||
|
"fieldname": "advance_tax_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Advance Tax Account",
|
||||||
|
"mandatory_depends_on": "eval:doc.apply_tax_withholding_amount",
|
||||||
|
"options": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.received_amount",
|
||||||
|
"fieldname": "received_amount_after_tax",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Received Amount After Tax",
|
||||||
|
"options": "paid_to_account_currency"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "doc.received_amount",
|
||||||
|
"fieldname": "base_received_amount_after_tax",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Received Amount After Tax (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-08 13:05:16.958866",
|
"modified": "2021-06-09 11:55:04.215050",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext, json
|
import frappe, erpnext, json
|
||||||
from frappe import _, scrub, ValidationError
|
from frappe import _, scrub, ValidationError, throw
|
||||||
from frappe.utils import flt, comma_or, nowdate, getdate
|
from frappe.utils import flt, comma_or, nowdate, getdate, cint
|
||||||
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
|
||||||
from erpnext.accounts.party import get_party_account
|
from erpnext.accounts.party import get_party_account
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account
|
||||||
@ -15,9 +15,11 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo
|
|||||||
from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
|
from erpnext.accounts.doctype.bank_account.bank_account import get_party_bank_account, get_bank_account_details
|
||||||
from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
|
from erpnext.controllers.accounts_controller import AccountsController, get_supplier_block_status
|
||||||
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
|
from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import get_party_account_based_on_invoice_discounting
|
||||||
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
from six import string_types, iteritems
|
from six import string_types, iteritems
|
||||||
|
|
||||||
|
from erpnext.controllers.accounts_controller import validate_taxes_and_charges
|
||||||
|
|
||||||
class InvalidPaymentEntry(ValidationError):
|
class InvalidPaymentEntry(ValidationError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -52,6 +54,8 @@ class PaymentEntry(AccountsController):
|
|||||||
self.set_exchange_rate()
|
self.set_exchange_rate()
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
self.validate_reference_documents()
|
self.validate_reference_documents()
|
||||||
|
self.set_tax_withholding()
|
||||||
|
self.apply_taxes()
|
||||||
self.set_amounts()
|
self.set_amounts()
|
||||||
self.clear_unallocated_reference_document_rows()
|
self.clear_unallocated_reference_document_rows()
|
||||||
self.validate_payment_against_negative_invoice()
|
self.validate_payment_against_negative_invoice()
|
||||||
@ -65,7 +69,6 @@ class PaymentEntry(AccountsController):
|
|||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.setup_party_account_field()
|
|
||||||
if self.difference_amount:
|
if self.difference_amount:
|
||||||
frappe.throw(_("Difference Amount must be zero"))
|
frappe.throw(_("Difference Amount must be zero"))
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
@ -78,7 +81,6 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
|
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
|
||||||
self.setup_party_account_field()
|
|
||||||
self.make_gl_entries(cancel=1)
|
self.make_gl_entries(cancel=1)
|
||||||
self.update_outstanding_amounts()
|
self.update_outstanding_amounts()
|
||||||
self.update_advance_paid()
|
self.update_advance_paid()
|
||||||
@ -122,6 +124,11 @@ class PaymentEntry(AccountsController):
|
|||||||
if flt(d.allocated_amount) > flt(d.outstanding_amount):
|
if flt(d.allocated_amount) > flt(d.outstanding_amount):
|
||||||
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
|
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
|
||||||
|
|
||||||
|
# Check for negative outstanding invoices as well
|
||||||
|
if flt(d.allocated_amount) < 0:
|
||||||
|
if flt(d.allocated_amount) < flt(d.outstanding_amount):
|
||||||
|
frappe.throw(_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx))
|
||||||
|
|
||||||
def delink_advance_entry_references(self):
|
def delink_advance_entry_references(self):
|
||||||
for reference in self.references:
|
for reference in self.references:
|
||||||
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
@ -177,7 +184,7 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
for field, value in iteritems(ref_details):
|
for field, value in iteritems(ref_details):
|
||||||
if field == 'exchange_rate' or not d.get(field) or force:
|
if field == 'exchange_rate' or not d.get(field) or force:
|
||||||
d.set(field, value)
|
d.db_set(field, value)
|
||||||
|
|
||||||
def validate_payment_type(self):
|
def validate_payment_type(self):
|
||||||
if self.payment_type not in ("Receive", "Pay", "Internal Transfer"):
|
if self.payment_type not in ("Receive", "Pay", "Internal Transfer"):
|
||||||
@ -307,7 +314,6 @@ class PaymentEntry(AccountsController):
|
|||||||
+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
|
+ "<br><br>" + _("If this is undesirable please cancel the corresponding Payment Entry."),
|
||||||
title=_("Warning"), indicator="orange")
|
title=_("Warning"), indicator="orange")
|
||||||
|
|
||||||
|
|
||||||
def validate_journal_entry(self):
|
def validate_journal_entry(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
if d.allocated_amount and d.reference_doctype == "Journal Entry":
|
||||||
@ -386,12 +392,98 @@ class PaymentEntry(AccountsController):
|
|||||||
else:
|
else:
|
||||||
self.status = 'Draft'
|
self.status = 'Draft'
|
||||||
|
|
||||||
|
self.db_set('status', self.status, update_modified = True)
|
||||||
|
|
||||||
|
def set_tax_withholding(self):
|
||||||
|
if not self.party_type == 'Supplier':
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.apply_tax_withholding_amount:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.advance_tax_account:
|
||||||
|
frappe.throw(_("Advance TDS account is mandatory for advance TDS deduction"))
|
||||||
|
|
||||||
|
reference_doclist = []
|
||||||
|
net_total = self.paid_amount
|
||||||
|
included_in_paid_amount = 0
|
||||||
|
|
||||||
|
# Adding args as purchase invoice to get TDS amount
|
||||||
|
args = frappe._dict({
|
||||||
|
'company': self.company,
|
||||||
|
'doctype': 'Purchase Invoice',
|
||||||
|
'supplier': self.party,
|
||||||
|
'posting_date': self.posting_date,
|
||||||
|
'net_total': net_total
|
||||||
|
})
|
||||||
|
|
||||||
|
tax_withholding_details = get_party_tax_withholding_details(args, self.tax_withholding_category)
|
||||||
|
|
||||||
|
if not tax_withholding_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
tax_withholding_details.update({
|
||||||
|
'included_in_paid_amount': included_in_paid_amount,
|
||||||
|
'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company)
|
||||||
|
})
|
||||||
|
|
||||||
|
accounts = []
|
||||||
|
for d in self.taxes:
|
||||||
|
if d.account_head == tax_withholding_details.get("account_head"):
|
||||||
|
|
||||||
|
# Preserve user updated included in paid amount
|
||||||
|
if d.included_in_paid_amount:
|
||||||
|
tax_withholding_details.update({'included_in_paid_amount': d.included_in_paid_amount})
|
||||||
|
|
||||||
|
d.update(tax_withholding_details)
|
||||||
|
accounts.append(d.account_head)
|
||||||
|
|
||||||
|
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||||
|
self.append("taxes", tax_withholding_details)
|
||||||
|
|
||||||
|
to_remove = [d for d in self.taxes
|
||||||
|
if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
|
||||||
|
|
||||||
|
for d in to_remove:
|
||||||
|
self.remove(d)
|
||||||
|
|
||||||
|
def apply_taxes(self):
|
||||||
|
self.initialize_taxes()
|
||||||
|
self.determine_exclusive_rate()
|
||||||
|
self.calculate_taxes()
|
||||||
|
|
||||||
def set_amounts(self):
|
def set_amounts(self):
|
||||||
|
self.set_received_amount()
|
||||||
self.set_amounts_in_company_currency()
|
self.set_amounts_in_company_currency()
|
||||||
|
self.set_amounts_after_tax()
|
||||||
self.set_total_allocated_amount()
|
self.set_total_allocated_amount()
|
||||||
self.set_unallocated_amount()
|
self.set_unallocated_amount()
|
||||||
self.set_difference_amount()
|
self.set_difference_amount()
|
||||||
|
|
||||||
|
def set_received_amount(self):
|
||||||
|
self.base_received_amount = self.base_paid_amount
|
||||||
|
|
||||||
|
def set_amounts_after_tax(self):
|
||||||
|
applicable_tax = 0
|
||||||
|
base_applicable_tax = 0
|
||||||
|
for tax in self.get('taxes'):
|
||||||
|
if not tax.included_in_paid_amount:
|
||||||
|
amount = -1 * tax.tax_amount if tax.add_deduct_tax == 'Deduct' else tax.tax_amount
|
||||||
|
base_amount = -1 * tax.base_tax_amount if tax.add_deduct_tax == 'Deduct' else tax.base_tax_amount
|
||||||
|
|
||||||
|
applicable_tax += amount
|
||||||
|
base_applicable_tax += base_amount
|
||||||
|
|
||||||
|
self.paid_amount_after_tax = flt(flt(self.paid_amount) + flt(applicable_tax),
|
||||||
|
self.precision("paid_amount_after_tax"))
|
||||||
|
self.base_paid_amount_after_tax = flt(flt(self.paid_amount_after_tax) * flt(self.source_exchange_rate),
|
||||||
|
self.precision("base_paid_amount_after_tax"))
|
||||||
|
|
||||||
|
self.received_amount_after_tax = flt(flt(self.received_amount) + flt(applicable_tax),
|
||||||
|
self.precision("paid_amount_after_tax"))
|
||||||
|
self.base_received_amount_after_tax = flt(flt(self.received_amount_after_tax) * flt(self.target_exchange_rate),
|
||||||
|
self.precision("base_paid_amount_after_tax"))
|
||||||
|
|
||||||
def set_amounts_in_company_currency(self):
|
def set_amounts_in_company_currency(self):
|
||||||
self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
|
self.base_paid_amount, self.base_received_amount, self.difference_amount = 0, 0, 0
|
||||||
if self.paid_amount:
|
if self.paid_amount:
|
||||||
@ -421,14 +513,14 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.party:
|
if self.party:
|
||||||
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
||||||
if self.payment_type == "Receive" \
|
if self.payment_type == "Receive" \
|
||||||
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
|
and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \
|
||||||
and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
|
and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate):
|
||||||
self.unallocated_amount = (self.base_received_amount + total_deductions -
|
self.unallocated_amount = (self.received_amount_after_tax + total_deductions -
|
||||||
self.base_total_allocated_amount) / self.source_exchange_rate
|
self.base_total_allocated_amount) / self.source_exchange_rate
|
||||||
elif self.payment_type == "Pay" \
|
elif self.payment_type == "Pay" \
|
||||||
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \
|
and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \
|
||||||
and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate):
|
and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate):
|
||||||
self.unallocated_amount = (self.base_paid_amount - (total_deductions +
|
self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions +
|
||||||
self.base_total_allocated_amount)) / self.target_exchange_rate
|
self.base_total_allocated_amount)) / self.target_exchange_rate
|
||||||
|
|
||||||
def set_difference_amount(self):
|
def set_difference_amount(self):
|
||||||
@ -438,11 +530,11 @@ class PaymentEntry(AccountsController):
|
|||||||
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
|
base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount)
|
||||||
|
|
||||||
if self.payment_type == "Receive":
|
if self.payment_type == "Receive":
|
||||||
self.difference_amount = base_party_amount - self.base_received_amount
|
self.difference_amount = base_party_amount - self.base_received_amount_after_tax
|
||||||
elif self.payment_type == "Pay":
|
elif self.payment_type == "Pay":
|
||||||
self.difference_amount = self.base_paid_amount - base_party_amount
|
self.difference_amount = self.base_paid_amount_after_tax - base_party_amount
|
||||||
else:
|
else:
|
||||||
self.difference_amount = self.base_paid_amount - flt(self.base_received_amount)
|
self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax)
|
||||||
|
|
||||||
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
||||||
|
|
||||||
@ -532,6 +624,7 @@ class PaymentEntry(AccountsController):
|
|||||||
self.add_party_gl_entries(gl_entries)
|
self.add_party_gl_entries(gl_entries)
|
||||||
self.add_bank_gl_entries(gl_entries)
|
self.add_bank_gl_entries(gl_entries)
|
||||||
self.add_deductions_gl_entries(gl_entries)
|
self.add_deductions_gl_entries(gl_entries)
|
||||||
|
self.add_tax_gl_entries(gl_entries)
|
||||||
|
|
||||||
make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
|
make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
|
||||||
|
|
||||||
@ -571,7 +664,7 @@ class PaymentEntry(AccountsController):
|
|||||||
gl_entries.append(gle)
|
gl_entries.append(gle)
|
||||||
|
|
||||||
if self.unallocated_amount:
|
if self.unallocated_amount:
|
||||||
base_unallocated_amount = base_unallocated_amount = self.unallocated_amount * \
|
base_unallocated_amount = self.unallocated_amount * \
|
||||||
(self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
|
(self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate)
|
||||||
|
|
||||||
gle = party_gl_dict.copy()
|
gle = party_gl_dict.copy()
|
||||||
@ -590,8 +683,8 @@ class PaymentEntry(AccountsController):
|
|||||||
"account": self.paid_from,
|
"account": self.paid_from,
|
||||||
"account_currency": self.paid_from_account_currency,
|
"account_currency": self.paid_from_account_currency,
|
||||||
"against": self.party if self.payment_type=="Pay" else self.paid_to,
|
"against": self.party if self.payment_type=="Pay" else self.paid_to,
|
||||||
"credit_in_account_currency": self.paid_amount,
|
"credit_in_account_currency": self.paid_amount_after_tax,
|
||||||
"credit": self.base_paid_amount,
|
"credit": self.base_paid_amount_after_tax,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, item=self)
|
}, item=self)
|
||||||
)
|
)
|
||||||
@ -601,12 +694,50 @@ class PaymentEntry(AccountsController):
|
|||||||
"account": self.paid_to,
|
"account": self.paid_to,
|
||||||
"account_currency": self.paid_to_account_currency,
|
"account_currency": self.paid_to_account_currency,
|
||||||
"against": self.party if self.payment_type=="Receive" else self.paid_from,
|
"against": self.party if self.payment_type=="Receive" else self.paid_from,
|
||||||
"debit_in_account_currency": self.received_amount,
|
"debit_in_account_currency": self.received_amount_after_tax,
|
||||||
"debit": self.base_received_amount,
|
"debit": self.base_received_amount_after_tax,
|
||||||
"cost_center": self.cost_center
|
"cost_center": self.cost_center
|
||||||
}, item=self)
|
}, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def add_tax_gl_entries(self, gl_entries):
|
||||||
|
for d in self.get('taxes'):
|
||||||
|
account_currency = get_account_currency(d.account_head)
|
||||||
|
if account_currency != self.company_currency:
|
||||||
|
frappe.throw(_("Currency for {0} must be {1}").format(d.account_head, self.company_currency))
|
||||||
|
|
||||||
|
if self.payment_type == 'Pay':
|
||||||
|
dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit"
|
||||||
|
elif self.payment_type == 'Receive':
|
||||||
|
dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit"
|
||||||
|
|
||||||
|
payment_or_advance_account = self.get_party_account_for_taxes()
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": d.account_head,
|
||||||
|
"against": self.party if self.payment_type=="Receive" else self.paid_from,
|
||||||
|
dr_or_cr: d.base_tax_amount,
|
||||||
|
dr_or_cr + "_in_account_currency": d.base_tax_amount
|
||||||
|
if account_currency==self.company_currency
|
||||||
|
else d.tax_amount,
|
||||||
|
"cost_center": d.cost_center
|
||||||
|
}, account_currency, item=d))
|
||||||
|
|
||||||
|
#Intentionally use -1 to get net values in party account
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": payment_or_advance_account,
|
||||||
|
"against": self.party if self.payment_type=="Receive" else self.paid_from,
|
||||||
|
dr_or_cr: -1 * d.base_tax_amount,
|
||||||
|
dr_or_cr + "_in_account_currency": -1*d.base_tax_amount
|
||||||
|
if account_currency==self.company_currency
|
||||||
|
else d.tax_amount,
|
||||||
|
"cost_center": self.cost_center,
|
||||||
|
"party_type": self.party_type,
|
||||||
|
"party": self.party
|
||||||
|
}, account_currency, item=d))
|
||||||
|
|
||||||
def add_deductions_gl_entries(self, gl_entries):
|
def add_deductions_gl_entries(self, gl_entries):
|
||||||
for d in self.get("deductions"):
|
for d in self.get("deductions"):
|
||||||
if d.amount:
|
if d.amount:
|
||||||
@ -625,6 +756,14 @@ class PaymentEntry(AccountsController):
|
|||||||
}, item=d)
|
}, item=d)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_party_account_for_taxes(self):
|
||||||
|
if self.advance_tax_account:
|
||||||
|
return self.advance_tax_account
|
||||||
|
elif self.payment_type == 'Receive':
|
||||||
|
return self.paid_from
|
||||||
|
elif self.payment_type == 'Pay':
|
||||||
|
return self.paid_to
|
||||||
|
|
||||||
def update_advance_paid(self):
|
def update_advance_paid(self):
|
||||||
if self.payment_type in ("Receive", "Pay") and self.party:
|
if self.payment_type in ("Receive", "Pay") and self.party:
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
@ -671,6 +810,139 @@ class PaymentEntry(AccountsController):
|
|||||||
self.append('deductions', row)
|
self.append('deductions', row)
|
||||||
self.set_unallocated_amount()
|
self.set_unallocated_amount()
|
||||||
|
|
||||||
|
def initialize_taxes(self):
|
||||||
|
for tax in self.get("taxes"):
|
||||||
|
validate_taxes_and_charges(tax)
|
||||||
|
validate_inclusive_tax(tax, self)
|
||||||
|
|
||||||
|
tax_fields = ["total", "tax_fraction_for_current_item", "grand_total_fraction_for_current_item"]
|
||||||
|
|
||||||
|
if tax.charge_type != "Actual":
|
||||||
|
tax_fields.append("tax_amount")
|
||||||
|
|
||||||
|
for fieldname in tax_fields:
|
||||||
|
tax.set(fieldname, 0.0)
|
||||||
|
|
||||||
|
self.paid_amount_after_tax = self.paid_amount
|
||||||
|
|
||||||
|
def determine_exclusive_rate(self):
|
||||||
|
if not any((cint(tax.included_in_paid_amount) for tax in self.get("taxes"))):
|
||||||
|
return
|
||||||
|
|
||||||
|
cumulated_tax_fraction = 0
|
||||||
|
for i, tax in enumerate(self.get("taxes")):
|
||||||
|
tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax)
|
||||||
|
if i==0:
|
||||||
|
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
|
||||||
|
else:
|
||||||
|
tax.grand_total_fraction_for_current_item = \
|
||||||
|
self.get("taxes")[i-1].grand_total_fraction_for_current_item \
|
||||||
|
+ tax.tax_fraction_for_current_item
|
||||||
|
|
||||||
|
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||||
|
|
||||||
|
self.paid_amount_after_tax = flt(self.paid_amount/(1+cumulated_tax_fraction))
|
||||||
|
|
||||||
|
def calculate_taxes(self):
|
||||||
|
self.total_taxes_and_charges = 0.0
|
||||||
|
self.base_total_taxes_and_charges = 0.0
|
||||||
|
|
||||||
|
actual_tax_dict = dict([[tax.idx, flt(tax.tax_amount, tax.precision("tax_amount"))]
|
||||||
|
for tax in self.get("taxes") if tax.charge_type == "Actual"])
|
||||||
|
|
||||||
|
for i, tax in enumerate(self.get('taxes')):
|
||||||
|
current_tax_amount = self.get_current_tax_amount(tax)
|
||||||
|
|
||||||
|
if tax.charge_type == "Actual":
|
||||||
|
actual_tax_dict[tax.idx] -= current_tax_amount
|
||||||
|
if i == len(self.get("taxes")) - 1:
|
||||||
|
current_tax_amount += actual_tax_dict[tax.idx]
|
||||||
|
|
||||||
|
tax.tax_amount = current_tax_amount
|
||||||
|
tax.base_tax_amount = tax.tax_amount * self.source_exchange_rate
|
||||||
|
|
||||||
|
if tax.add_deduct_tax == "Deduct":
|
||||||
|
current_tax_amount *= -1.0
|
||||||
|
else:
|
||||||
|
current_tax_amount *= 1.0
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
tax.total = flt(self.paid_amount_after_tax + current_tax_amount, self.precision("total", tax))
|
||||||
|
else:
|
||||||
|
tax.total = flt(self.get('taxes')[i-1].total + current_tax_amount, self.precision("total", tax))
|
||||||
|
|
||||||
|
tax.base_total = tax.total * self.source_exchange_rate
|
||||||
|
|
||||||
|
self.total_taxes_and_charges += current_tax_amount
|
||||||
|
self.base_total_taxes_and_charges += current_tax_amount * self.source_exchange_rate
|
||||||
|
|
||||||
|
if self.get('taxes'):
|
||||||
|
self.paid_amount_after_tax = self.get('taxes')[-1].base_total
|
||||||
|
|
||||||
|
def get_current_tax_amount(self, tax):
|
||||||
|
tax_rate = tax.rate
|
||||||
|
|
||||||
|
# To set row_id by default as previous row.
|
||||||
|
if tax.charge_type in ["On Previous Row Amount", "On Previous Row Total"]:
|
||||||
|
if tax.idx == 1:
|
||||||
|
frappe.throw(_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
|
||||||
|
|
||||||
|
if not tax.row_id:
|
||||||
|
tax.row_id = tax.idx - 1
|
||||||
|
|
||||||
|
if tax.charge_type == "Actual":
|
||||||
|
current_tax_amount = flt(tax.tax_amount, self.precision("tax_amount", tax))
|
||||||
|
elif tax.charge_type == "On Paid Amount":
|
||||||
|
current_tax_amount = (tax_rate / 100.0) * self.paid_amount_after_tax
|
||||||
|
elif tax.charge_type == "On Previous Row Amount":
|
||||||
|
current_tax_amount = (tax_rate / 100.0) * \
|
||||||
|
self.get('taxes')[cint(tax.row_id) - 1].tax_amount
|
||||||
|
|
||||||
|
elif tax.charge_type == "On Previous Row Total":
|
||||||
|
current_tax_amount = (tax_rate / 100.0) * \
|
||||||
|
self.get('taxes')[cint(tax.row_id) - 1].total
|
||||||
|
|
||||||
|
return current_tax_amount
|
||||||
|
|
||||||
|
def get_current_tax_fraction(self, tax):
|
||||||
|
current_tax_fraction = 0
|
||||||
|
|
||||||
|
if cint(tax.included_in_paid_amount):
|
||||||
|
tax_rate = tax.rate
|
||||||
|
|
||||||
|
if tax.charge_type == "On Paid Amount":
|
||||||
|
current_tax_fraction = tax_rate / 100.0
|
||||||
|
elif tax.charge_type == "On Previous Row Amount":
|
||||||
|
current_tax_fraction = (tax_rate / 100.0) * \
|
||||||
|
self.get("taxes")[cint(tax.row_id) - 1].tax_fraction_for_current_item
|
||||||
|
elif tax.charge_type == "On Previous Row Total":
|
||||||
|
current_tax_fraction = (tax_rate / 100.0) * \
|
||||||
|
self.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
|
||||||
|
|
||||||
|
if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
|
||||||
|
current_tax_fraction *= -1.0
|
||||||
|
|
||||||
|
return current_tax_fraction
|
||||||
|
|
||||||
|
def validate_inclusive_tax(tax, doc):
|
||||||
|
def _on_previous_row_error(row_range):
|
||||||
|
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
|
||||||
|
|
||||||
|
if cint(getattr(tax, "included_in_paid_amount", None)):
|
||||||
|
if tax.charge_type == "Actual":
|
||||||
|
# inclusive tax cannot be of type Actual
|
||||||
|
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
|
||||||
|
elif tax.charge_type == "On Previous Row Amount" and \
|
||||||
|
not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_paid_amount):
|
||||||
|
# referred row should also be inclusive
|
||||||
|
_on_previous_row_error(tax.row_id)
|
||||||
|
elif tax.charge_type == "On Previous Row Total" and \
|
||||||
|
not all([cint(t.included_in_paid_amount for t in doc.get("taxes")[:cint(tax.row_id) - 1])]):
|
||||||
|
# all rows about the referred tax should be inclusive
|
||||||
|
_on_previous_row_error("1 - %d" % (cint(tax.row_id),))
|
||||||
|
elif tax.get("category") == "Valuation":
|
||||||
|
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding_reference_documents(args):
|
def get_outstanding_reference_documents(args):
|
||||||
|
|
||||||
@ -989,6 +1261,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
|||||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||||
elif reference_doctype == "Donation":
|
elif reference_doctype == "Donation":
|
||||||
total_amount = ref_doc.get("amount")
|
total_amount = ref_doc.get("amount")
|
||||||
|
outstanding_amount = total_amount
|
||||||
exchange_rate = 1
|
exchange_rate = 1
|
||||||
elif reference_doctype == "Dunning":
|
elif reference_doctype == "Dunning":
|
||||||
total_amount = ref_doc.get("dunning_amount")
|
total_amount = ref_doc.get("dunning_amount")
|
||||||
@ -1235,6 +1508,13 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
})
|
})
|
||||||
pe.set_difference_amount()
|
pe.set_difference_amount()
|
||||||
|
|
||||||
|
if doc.doctype == 'Purchase Order' and doc.apply_tds:
|
||||||
|
pe.apply_tax_withholding_amount = 1
|
||||||
|
pe.tax_withholding_category = doc.tax_withholding_category
|
||||||
|
|
||||||
|
if not pe.advance_tax_account:
|
||||||
|
pe.advance_tax_account = frappe.db.get_value('Company', pe.company, 'unrealized_profit_loss_account')
|
||||||
|
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
def get_bank_cash_account(doc, bank_account):
|
def get_bank_cash_account(doc, bank_account):
|
||||||
@ -1353,6 +1633,13 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
|
|||||||
paid_amount = received_amount * doc.get('conversion_rate', 1)
|
paid_amount = received_amount * doc.get('conversion_rate', 1)
|
||||||
if dt == "Employee Advance":
|
if dt == "Employee Advance":
|
||||||
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
||||||
|
|
||||||
|
if dt == "Purchase Order" and doc.apply_tds:
|
||||||
|
if party_account_currency == bank.account_currency:
|
||||||
|
paid_amount = received_amount = doc.base_net_total
|
||||||
|
else:
|
||||||
|
paid_amount = received_amount = doc.base_net_total * doc.get('exchange_rate', 1)
|
||||||
|
|
||||||
return paid_amount, received_amount
|
return paid_amount, received_amount
|
||||||
|
|
||||||
def apply_early_payment_discount(paid_amount, received_amount, doc):
|
def apply_early_payment_discount(paid_amount, received_amount, doc):
|
||||||
|
@ -1,140 +1,70 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-06-15 15:56:30.815503",
|
"creation": "2016-06-15 15:56:30.815503",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"field_order": [
|
||||||
|
"account",
|
||||||
|
"cost_center",
|
||||||
|
"amount",
|
||||||
|
"column_break_2",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Account",
|
"label": "Account",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"show_days": 1,
|
||||||
"set_only_once": 0,
|
"show_seconds": 1
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "cost_center",
|
"fieldname": "cost_center",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Cost Center",
|
"label": "Cost Center",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Cost Center",
|
"options": "Cost Center",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"show_days": 1,
|
||||||
"set_only_once": 0,
|
"show_seconds": 1
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"show_days": 1,
|
||||||
"set_only_once": 0,
|
"show_seconds": 1
|
||||||
"translatable": 0,
|
},
|
||||||
"unique": 0
|
{
|
||||||
|
"fieldname": "column_break_2",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"label": "Description",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-01-07 16:52:07.040146",
|
"modified": "2020-09-12 20:38:08.110674",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Deduction",
|
"name": "Payment Entry Deduction",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -492,7 +492,6 @@ def update_payment_req_status(doc, method):
|
|||||||
status = 'Requested'
|
status = 'Requested'
|
||||||
|
|
||||||
pay_req_doc.db_set('status', status)
|
pay_req_doc.db_set('status', status)
|
||||||
frappe.db.commit()
|
|
||||||
|
|
||||||
def get_dummy_message(doc):
|
def get_dummy_message(doc):
|
||||||
return frappe.render_template("""{% if doc.contact_person -%}
|
return frappe.render_template("""{% if doc.contact_person -%}
|
||||||
|
@ -1,297 +1,102 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "ACC-PCV-.YYYY.-.#####",
|
"autoname": "ACC-PCV-.YYYY.-.#####",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-01-10 16:34:07",
|
"creation": "2013-01-10 16:34:07",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 0,
|
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"transaction_date",
|
||||||
|
"posting_date",
|
||||||
|
"fiscal_year",
|
||||||
|
"amended_from",
|
||||||
|
"company",
|
||||||
|
"cost_center_wise_pnl",
|
||||||
|
"column_break1",
|
||||||
|
"closing_account_head",
|
||||||
|
"remarks"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "transaction_date",
|
"fieldname": "transaction_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Transaction Date",
|
"label": "Transaction Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "transaction_date",
|
"oldfieldname": "transaction_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "posting_date",
|
"oldfieldname": "posting_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "fiscal_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Closing Fiscal Year",
|
"label": "Closing Fiscal Year",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "fiscal_year",
|
"oldfieldname": "fiscal_year",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "amended_from",
|
"oldfieldname": "amended_from",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "Period Closing Voucher",
|
"options": "Period Closing Voucher",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "company",
|
"oldfieldname": "company",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break1",
|
"fieldname": "column_break1",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Column Break"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Column Break",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"description": "The account head under Liability or Equity, in which Profit/Loss will be booked",
|
"description": "The account head under Liability or Equity, in which Profit/Loss will be booked",
|
||||||
"fieldname": "closing_account_head",
|
"fieldname": "closing_account_head",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Closing Account Head",
|
"label": "Closing Account Head",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "closing_account_head",
|
"oldfieldname": "closing_account_head",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Account",
|
"options": "Account",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "remarks",
|
"fieldname": "remarks",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Remarks",
|
"label": "Remarks",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "remarks",
|
"oldfieldname": "remarks",
|
||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
},
|
||||||
"print_hide_if_no_value": 0,
|
{
|
||||||
"read_only": 0,
|
"default": "0",
|
||||||
"remember_last_selected_value": 0,
|
"fieldname": "cost_center_wise_pnl",
|
||||||
"report_hide": 0,
|
"fieldtype": "Check",
|
||||||
"reqd": 1,
|
"label": "Book Cost Center Wise Profit/Loss"
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [],
|
||||||
"istable": 0,
|
"modified": "2021-05-20 15:27:37.210458",
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2020-09-18 17:26:09.703215",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Period Closing Voucher",
|
"name": "Period Closing Voucher",
|
||||||
@ -303,15 +108,10 @@
|
|||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
@ -322,29 +122,17 @@
|
|||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Accounts Manager",
|
"role": "Accounts Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"search_fields": "posting_date, fiscal_year",
|
"search_fields": "posting_date, fiscal_year",
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "closing_account_head",
|
"title_field": "closing_account_head"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -52,35 +52,35 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
net_pl_balance = 0
|
net_pl_balance = 0
|
||||||
dimension_fields = ['t1.cost_center']
|
|
||||||
|
|
||||||
accounting_dimensions = get_accounting_dimensions()
|
pl_accounts = self.get_pl_balances()
|
||||||
for dimension in accounting_dimensions:
|
|
||||||
dimension_fields.append('t1.{0}'.format(dimension))
|
|
||||||
|
|
||||||
dimension_filters, default_dimensions = get_dimensions()
|
|
||||||
|
|
||||||
pl_accounts = self.get_pl_balances(dimension_fields)
|
|
||||||
|
|
||||||
for acc in pl_accounts:
|
for acc in pl_accounts:
|
||||||
if flt(acc.balance_in_company_currency):
|
if flt(acc.bal_in_company_currency):
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": acc.account,
|
"account": acc.account,
|
||||||
"cost_center": acc.cost_center,
|
"cost_center": acc.cost_center,
|
||||||
"account_currency": acc.account_currency,
|
"account_currency": acc.account_currency,
|
||||||
"debit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
|
||||||
if flt(acc.balance_in_account_currency) < 0 else 0,
|
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
|
||||||
"debit": abs(flt(acc.balance_in_company_currency)) \
|
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
|
||||||
if flt(acc.balance_in_company_currency) < 0 else 0,
|
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
|
||||||
"credit_in_account_currency": abs(flt(acc.balance_in_account_currency)) \
|
|
||||||
if flt(acc.balance_in_account_currency) > 0 else 0,
|
|
||||||
"credit": abs(flt(acc.balance_in_company_currency)) \
|
|
||||||
if flt(acc.balance_in_company_currency) > 0 else 0
|
|
||||||
}, item=acc))
|
}, item=acc))
|
||||||
|
|
||||||
net_pl_balance += flt(acc.balance_in_company_currency)
|
net_pl_balance += flt(acc.bal_in_company_currency)
|
||||||
|
|
||||||
if net_pl_balance:
|
if net_pl_balance:
|
||||||
|
if self.cost_center_wise_pnl:
|
||||||
|
costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts)
|
||||||
|
gl_entries += costcenter_wise_gl_entries
|
||||||
|
else:
|
||||||
|
gl_entry = self.get_pnl_gl_entry(net_pl_balance)
|
||||||
|
gl_entries.append(gl_entry)
|
||||||
|
|
||||||
|
from erpnext.accounts.general_ledger import make_gl_entries
|
||||||
|
make_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
def get_pnl_gl_entry(self, net_pl_balance):
|
||||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
gl_entry = self.get_gl_dict({
|
gl_entry = self.get_gl_dict({
|
||||||
"account": self.closing_account_head,
|
"account": self.closing_account_head,
|
||||||
@ -91,23 +91,56 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
"cost_center": cost_center
|
"cost_center": cost_center
|
||||||
})
|
})
|
||||||
|
|
||||||
for dimension in accounting_dimensions:
|
self.update_default_dimensions(gl_entry)
|
||||||
|
|
||||||
|
return gl_entry
|
||||||
|
|
||||||
|
def get_costcenter_wise_pnl_gl_entries(self, pl_accounts):
|
||||||
|
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
|
gl_entries = []
|
||||||
|
|
||||||
|
for acc in pl_accounts:
|
||||||
|
if flt(acc.bal_in_company_currency):
|
||||||
|
gl_entry = self.get_gl_dict({
|
||||||
|
"account": self.closing_account_head,
|
||||||
|
"cost_center": acc.cost_center or company_cost_center,
|
||||||
|
"account_currency": acc.account_currency,
|
||||||
|
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
|
||||||
|
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
|
||||||
|
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
|
||||||
|
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0
|
||||||
|
}, item=acc)
|
||||||
|
|
||||||
|
self.update_default_dimensions(gl_entry)
|
||||||
|
|
||||||
|
gl_entries.append(gl_entry)
|
||||||
|
|
||||||
|
return gl_entries
|
||||||
|
|
||||||
|
def update_default_dimensions(self, gl_entry):
|
||||||
|
if not self.accounting_dimensions:
|
||||||
|
self.accounting_dimensions = get_accounting_dimensions()
|
||||||
|
|
||||||
|
_, default_dimensions = get_dimensions()
|
||||||
|
for dimension in self.accounting_dimensions:
|
||||||
gl_entry.update({
|
gl_entry.update({
|
||||||
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
dimension: default_dimensions.get(self.company, {}).get(dimension)
|
||||||
})
|
})
|
||||||
|
|
||||||
gl_entries.append(gl_entry)
|
def get_pl_balances(self):
|
||||||
|
"""Get balance for dimension-wise pl accounts"""
|
||||||
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
dimension_fields = ['t1.cost_center']
|
||||||
make_gl_entries(gl_entries)
|
|
||||||
|
self.accounting_dimensions = get_accounting_dimensions()
|
||||||
|
for dimension in self.accounting_dimensions:
|
||||||
|
dimension_fields.append('t1.{0}'.format(dimension))
|
||||||
|
|
||||||
def get_pl_balances(self, dimension_fields):
|
|
||||||
"""Get balance for pl accounts"""
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select
|
select
|
||||||
t1.account, t2.account_currency, {dimension_fields},
|
t1.account, t2.account_currency, {dimension_fields},
|
||||||
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as balance_in_account_currency,
|
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
|
||||||
sum(t1.debit) - sum(t1.credit) as balance_in_company_currency
|
sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
|
||||||
from `tabGL Entry` t1, `tabAccount` t2
|
from `tabGL Entry` t1, `tabAccount` t2
|
||||||
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
where t1.account = t2.name and t2.report_type = 'Profit and Loss'
|
||||||
and t2.docstatus < 2 and t2.company = %s
|
and t2.docstatus < 2 and t2.company = %s
|
||||||
|
@ -8,6 +8,7 @@ import frappe
|
|||||||
from frappe.utils import flt, today
|
from frappe.utils import flt, today
|
||||||
from erpnext.accounts.utils import get_fiscal_year, now
|
from erpnext.accounts.utils import get_fiscal_year, now
|
||||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||||
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||||
def test_closing_entry(self):
|
def test_closing_entry(self):
|
||||||
@ -65,6 +66,58 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
|||||||
self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
|
self.assertEqual(gle_for_random_expense_account[0].amount_in_account_currency,
|
||||||
-1*random_expense_account[0].balance_in_account_currency)
|
-1*random_expense_account[0].balance_in_account_currency)
|
||||||
|
|
||||||
|
def test_cost_center_wise_posting(self):
|
||||||
|
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
|
||||||
|
|
||||||
|
company = create_company()
|
||||||
|
surplus_account = create_account()
|
||||||
|
|
||||||
|
cost_center1 = create_cost_center("Test Cost Center 1")
|
||||||
|
cost_center2 = create_cost_center("Test Cost Center 2")
|
||||||
|
|
||||||
|
create_sales_invoice(
|
||||||
|
company=company,
|
||||||
|
cost_center=cost_center1,
|
||||||
|
income_account="Sales - TPC",
|
||||||
|
expense_account="Cost of Goods Sold - TPC",
|
||||||
|
rate=400,
|
||||||
|
debit_to="Debtors - TPC"
|
||||||
|
)
|
||||||
|
create_sales_invoice(
|
||||||
|
company=company,
|
||||||
|
cost_center=cost_center2,
|
||||||
|
income_account="Sales - TPC",
|
||||||
|
expense_account="Cost of Goods Sold - TPC",
|
||||||
|
rate=200,
|
||||||
|
debit_to="Debtors - TPC"
|
||||||
|
)
|
||||||
|
|
||||||
|
pcv = frappe.get_doc({
|
||||||
|
"transaction_date": today(),
|
||||||
|
"posting_date": today(),
|
||||||
|
"fiscal_year": get_fiscal_year(today())[0],
|
||||||
|
"company": "Test PCV Company",
|
||||||
|
"cost_center_wise_pnl": 1,
|
||||||
|
"closing_account_head": surplus_account,
|
||||||
|
"remarks": "Test",
|
||||||
|
"doctype": "Period Closing Voucher"
|
||||||
|
})
|
||||||
|
pcv.insert()
|
||||||
|
pcv.submit()
|
||||||
|
|
||||||
|
expected_gle = (
|
||||||
|
('Sales - TPC', 200.0, 0.0, cost_center2),
|
||||||
|
(surplus_account, 0.0, 200.0, cost_center2),
|
||||||
|
('Sales - TPC', 400.0, 0.0, cost_center1),
|
||||||
|
(surplus_account, 0.0, 400.0, cost_center1)
|
||||||
|
)
|
||||||
|
|
||||||
|
pcv_gle = frappe.db.sql("""
|
||||||
|
select account, debit, credit, cost_center from `tabGL Entry` where voucher_no=%s
|
||||||
|
""", (pcv.name))
|
||||||
|
|
||||||
|
self.assertTrue(pcv_gle, expected_gle)
|
||||||
|
|
||||||
def make_period_closing_voucher(self):
|
def make_period_closing_voucher(self):
|
||||||
pcv = frappe.get_doc({
|
pcv = frappe.get_doc({
|
||||||
"doctype": "Period Closing Voucher",
|
"doctype": "Period Closing Voucher",
|
||||||
@ -80,6 +133,38 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
|||||||
|
|
||||||
return pcv
|
return pcv
|
||||||
|
|
||||||
|
def create_company():
|
||||||
|
company = frappe.get_doc({
|
||||||
|
'doctype': 'Company',
|
||||||
|
'company_name': "Test PCV Company",
|
||||||
|
'country': 'United States',
|
||||||
|
'default_currency': 'USD'
|
||||||
|
})
|
||||||
|
company.insert(ignore_if_duplicate = True)
|
||||||
|
return company.name
|
||||||
|
|
||||||
|
def create_account():
|
||||||
|
account = frappe.get_doc({
|
||||||
|
"account_name": "Reserve and Surplus",
|
||||||
|
"is_group": 0,
|
||||||
|
"company": "Test PCV Company",
|
||||||
|
"root_type": "Liability",
|
||||||
|
"report_type": "Balance Sheet",
|
||||||
|
"account_currency": "USD",
|
||||||
|
"parent_account": "Current Liabilities - TPC",
|
||||||
|
"doctype": "Account"
|
||||||
|
}).insert(ignore_if_duplicate = True)
|
||||||
|
return account.name
|
||||||
|
|
||||||
|
def create_cost_center(cc_name):
|
||||||
|
costcenter = frappe.get_doc({
|
||||||
|
"company": "Test PCV Company",
|
||||||
|
"cost_center_name": cc_name,
|
||||||
|
"doctype": "Cost Center",
|
||||||
|
"parent_cost_center": "Test PCV Company - TPC"
|
||||||
|
})
|
||||||
|
costcenter.insert(ignore_if_duplicate = True)
|
||||||
|
return costcenter.name
|
||||||
|
|
||||||
test_dependencies = ["Customer", "Cost Center"]
|
test_dependencies = ["Customer", "Cost Center"]
|
||||||
test_records = frappe.get_test_records("Period Closing Voucher")
|
test_records = frappe.get_test_records("Period Closing Voucher")
|
||||||
|
@ -107,7 +107,7 @@ frappe.ui.form.on('POS Closing Entry', {
|
|||||||
frm.set_value("taxes", []);
|
frm.set_value("taxes", []);
|
||||||
|
|
||||||
for (let row of frm.doc.payment_reconciliation) {
|
for (let row of frm.doc.payment_reconciliation) {
|
||||||
row.expected_amount = 0;
|
row.expected_amount = row.opening_amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let row of frm.doc.pos_transactions) {
|
for (let row of frm.doc.pos_transactions) {
|
||||||
@ -154,6 +154,9 @@ function add_to_pos_transaction(d, frm) {
|
|||||||
function refresh_payments(d, frm) {
|
function refresh_payments(d, frm) {
|
||||||
d.payments.forEach(p => {
|
d.payments.forEach(p => {
|
||||||
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
||||||
|
if (p.account == d.account_for_change_amount) {
|
||||||
|
p.amount -= flt(d.change_amount);
|
||||||
|
}
|
||||||
if (payment) {
|
if (payment) {
|
||||||
payment.expected_amount += flt(p.amount);
|
payment.expected_amount += flt(p.amount);
|
||||||
payment.difference = payment.closing_amount - payment.expected_amount;
|
payment.difference = payment.closing_amount - payment.expected_amount;
|
||||||
|
@ -140,6 +140,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
return
|
return
|
||||||
|
|
||||||
available_stock = get_stock_availability(d.item_code, d.warehouse)
|
available_stock = get_stock_availability(d.item_code, d.warehouse)
|
||||||
|
|
||||||
item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)
|
item_code, warehouse, qty = frappe.bold(d.item_code), frappe.bold(d.warehouse), frappe.bold(d.qty)
|
||||||
if flt(available_stock) <= 0:
|
if flt(available_stock) <= 0:
|
||||||
frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.')
|
frappe.throw(_('Row #{}: Item Code: {} is not available under warehouse {}.')
|
||||||
@ -213,6 +214,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
|
is_stock_item = frappe.get_cached_value("Item", d.get("item_code"), "is_stock_item")
|
||||||
if not is_stock_item:
|
if not is_stock_item:
|
||||||
|
if not frappe.db.exists('Product Bundle', d.item_code):
|
||||||
frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice.")
|
frappe.throw(_("Row #{}: Item {} is a non stock item. You can only include stock items in a POS Invoice.")
|
||||||
.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
|
.format(d.idx, frappe.bold(d.item_code)), title=_("Invalid Item"))
|
||||||
|
|
||||||
@ -455,15 +457,36 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_stock_availability(item_code, warehouse):
|
def get_stock_availability(item_code, warehouse):
|
||||||
|
if frappe.db.get_value('Item', item_code, 'is_stock_item'):
|
||||||
|
bin_qty = get_bin_qty(item_code, warehouse)
|
||||||
|
pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
|
||||||
|
return bin_qty - pos_sales_qty
|
||||||
|
else:
|
||||||
|
if frappe.db.exists('Product Bundle', item_code):
|
||||||
|
return get_bundle_availability(item_code, warehouse)
|
||||||
|
|
||||||
|
def get_bundle_availability(bundle_item_code, warehouse):
|
||||||
|
product_bundle = frappe.get_doc('Product Bundle', bundle_item_code)
|
||||||
|
|
||||||
|
bundle_bin_qty = 1000000
|
||||||
|
for item in product_bundle.items:
|
||||||
|
item_bin_qty = get_bin_qty(item.item_code, warehouse)
|
||||||
|
item_pos_reserved_qty = get_pos_reserved_qty(item.item_code, warehouse)
|
||||||
|
available_qty = item_bin_qty - item_pos_reserved_qty
|
||||||
|
|
||||||
|
max_available_bundles = available_qty / item.qty
|
||||||
|
if bundle_bin_qty > max_available_bundles:
|
||||||
|
bundle_bin_qty = max_available_bundles
|
||||||
|
|
||||||
|
pos_sales_qty = get_pos_reserved_qty(bundle_item_code, warehouse)
|
||||||
|
return bundle_bin_qty - pos_sales_qty
|
||||||
|
|
||||||
|
def get_bin_qty(item_code, warehouse):
|
||||||
bin_qty = frappe.db.sql("""select actual_qty from `tabBin`
|
bin_qty = frappe.db.sql("""select actual_qty from `tabBin`
|
||||||
where item_code = %s and warehouse = %s
|
where item_code = %s and warehouse = %s
|
||||||
limit 1""", (item_code, warehouse), as_dict=1)
|
limit 1""", (item_code, warehouse), as_dict=1)
|
||||||
|
|
||||||
pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
|
return bin_qty[0].actual_qty or 0 if bin_qty else 0
|
||||||
|
|
||||||
bin_qty = bin_qty[0].actual_qty or 0 if bin_qty else 0
|
|
||||||
|
|
||||||
return bin_qty - pos_sales_qty
|
|
||||||
|
|
||||||
def get_pos_reserved_qty(item_code, warehouse):
|
def get_pos_reserved_qty(item_code, warehouse):
|
||||||
reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
|
reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
|
||||||
|
@ -152,7 +152,7 @@ class PricingRule(Document):
|
|||||||
frappe.throw(_("Valid from date must be less than valid upto date"))
|
frappe.throw(_("Valid from date must be less than valid upto date"))
|
||||||
|
|
||||||
def validate_condition(self):
|
def validate_condition(self):
|
||||||
if self.condition and ("=" in self.condition) and re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", self.condition):
|
if self.condition and ("=" in self.condition) and re.match(r'[\w\.:_]+\s*={1}\s*[\w\.@\'"]+', self.condition):
|
||||||
frappe.throw(_("Invalid condition expression"))
|
frappe.throw(_("Invalid condition expression"))
|
||||||
|
|
||||||
#--------------------------------------------------------------------------------
|
#--------------------------------------------------------------------------------
|
||||||
|
@ -19,7 +19,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
|||||||
frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'});
|
frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'});
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
frappe.msgprint('No Records for these settings.')
|
frappe.msgprint(__('No Records for these settings.'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -33,7 +33,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
|||||||
type: 'GET',
|
type: 'GET',
|
||||||
success: function(result) {
|
success: function(result) {
|
||||||
if(jQuery.isEmptyObject(result)){
|
if(jQuery.isEmptyObject(result)){
|
||||||
frappe.msgprint('No Records for these settings.');
|
frappe.msgprint(__('No Records for these settings.'));
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
window.location = url;
|
window.location = url;
|
||||||
@ -92,7 +92,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
|
|||||||
frm.refresh_field('customers');
|
frm.refresh_field('customers');
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
frappe.throw('No Customers found with selected options.');
|
frappe.throw(__('No Customers found with selected options.'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -68,9 +68,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
super(PurchaseInvoice, self).validate()
|
super(PurchaseInvoice, self).validate()
|
||||||
|
|
||||||
# apply tax withholding only if checked and applicable
|
|
||||||
self.set_tax_withholding()
|
|
||||||
|
|
||||||
if not self.is_return:
|
if not self.is_return:
|
||||||
self.po_required()
|
self.po_required()
|
||||||
self.pr_required()
|
self.pr_required()
|
||||||
@ -251,11 +248,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
if self.update_stock and (not item.from_warehouse):
|
if self.update_stock and (not item.from_warehouse):
|
||||||
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
|
if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]:
|
||||||
msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]))
|
msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format(
|
||||||
msg += _("because account {} is not linked to warehouse {} ").format(frappe.bold(item.expense_account), frappe.bold(item.warehouse))
|
item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse))
|
||||||
msg += _("or it is not the default inventory account")
|
|
||||||
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
||||||
|
|
||||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||||
else:
|
else:
|
||||||
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
|
# check if 'Stock Received But Not Billed' account is credited in Purchase receipt or not
|
||||||
@ -266,8 +261,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
|
|
||||||
if negative_expense_booked_in_pr:
|
if negative_expense_booked_in_pr:
|
||||||
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
||||||
msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
|
msg = _("Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}").format(
|
||||||
msg += _("because expense is booked against this account in Purchase Receipt {}").format(frappe.bold(item.purchase_receipt))
|
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt))
|
||||||
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
||||||
|
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
@ -275,8 +270,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
# If no purchase receipt present then book expense in 'Stock Received But Not Billed'
|
||||||
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
# This is done in cases when Purchase Invoice is created before Purchase Receipt
|
||||||
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
if for_validate and item.expense_account and item.expense_account != stock_not_billed_account:
|
||||||
msg = _("Row {}: Expense Head changed to {} ").format(item.idx, frappe.bold(stock_not_billed_account))
|
msg = _("Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}.").format(
|
||||||
msg += _("as no Purchase Receipt is created against Item {}. ").format(frappe.bold(item.item_code))
|
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.item_code))
|
||||||
|
msg += "<br>"
|
||||||
msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice")
|
msg += _("This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice")
|
||||||
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
frappe.msgprint(msg, title=_("Expense Head Changed"))
|
||||||
|
|
||||||
@ -308,8 +304,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not d.purchase_order:
|
if not d.purchase_order:
|
||||||
msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
|
msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
|
||||||
msg += "<br><br>"
|
msg += "<br><br>"
|
||||||
msg += _("To submit the invoice without purchase order please set {} ").format(frappe.bold(_('Purchase Order Required')))
|
msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format(
|
||||||
msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
|
frappe.bold(_('Purchase Order Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
|
||||||
throw(msg, title=_("Mandatory Purchase Order"))
|
throw(msg, title=_("Mandatory Purchase Order"))
|
||||||
|
|
||||||
def pr_required(self):
|
def pr_required(self):
|
||||||
@ -323,8 +319,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if not d.purchase_receipt and d.item_code in stock_items:
|
if not d.purchase_receipt and d.item_code in stock_items:
|
||||||
msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
|
msg = _("Purchase Receipt Required for item {}").format(frappe.bold(d.item_code))
|
||||||
msg += "<br><br>"
|
msg += "<br><br>"
|
||||||
msg += _("To submit the invoice without purchase receipt please set {} ").format(frappe.bold(_('Purchase Receipt Required')))
|
msg += _("To submit the invoice without purchase receipt please set {0} as {1} in {2}").format(
|
||||||
msg += _("as {} in {}").format(frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
|
frappe.bold(_('Purchase Receipt Required')), frappe.bold('No'), get_link_to_form('Buying Settings', 'Buying Settings', 'Buying Settings'))
|
||||||
throw(msg, title=_("Mandatory Purchase Receipt"))
|
throw(msg, title=_("Mandatory Purchase Receipt"))
|
||||||
|
|
||||||
def validate_write_off_account(self):
|
def validate_write_off_account(self):
|
||||||
@ -456,6 +452,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
self.make_internal_transfer_gl_entries(gl_entries)
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
self.allocate_advance_taxes(gl_entries)
|
||||||
|
|
||||||
gl_entries = make_regional_gl_entries(gl_entries, self)
|
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||||
|
|
||||||
gl_entries = merge_similar_entries(gl_entries)
|
gl_entries = merge_similar_entries(gl_entries)
|
||||||
@ -1090,6 +1088,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
for d in self.taxes:
|
for d in self.taxes:
|
||||||
if d.account_head == tax_withholding_details.get("account_head"):
|
if d.account_head == tax_withholding_details.get("account_head"):
|
||||||
d.update(tax_withholding_details)
|
d.update(tax_withholding_details)
|
||||||
|
|
||||||
accounts.append(d.account_head)
|
accounts.append(d.account_head)
|
||||||
|
|
||||||
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||||
|
@ -16,6 +16,7 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import get_qty_after_tra
|
|||||||
from erpnext.projects.doctype.project.test_project import make_project
|
from erpnext.projects.doctype.project.test_project import make_project
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account, create_account
|
||||||
from erpnext.stock.doctype.item.test_item import create_item
|
from erpnext.stock.doctype.item.test_item import create_item
|
||||||
|
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||||
|
|
||||||
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
|
test_dependencies = ["Item", "Cost Center", "Payment Term", "Payment Terms Template"]
|
||||||
test_ignore = ["Serial No"]
|
test_ignore = ["Serial No"]
|
||||||
@ -636,8 +637,8 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
def test_rejected_serial_no(self):
|
def test_rejected_serial_no(self):
|
||||||
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
|
pi = make_purchase_invoice(item_code="_Test Serialized Item With Series", received_qty=2, qty=1,
|
||||||
rejected_qty=1, rate=500, update_stock=1,
|
rejected_qty=1, rate=500, update_stock=1, rejected_warehouse = "_Test Rejected Warehouse - _TC",
|
||||||
rejected_warehouse = "_Test Rejected Warehouse - _TC")
|
allow_zero_valuation_rate=1)
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
|
self.assertEqual(frappe.db.get_value("Serial No", pi.get("items")[0].serial_no, "warehouse"),
|
||||||
pi.get("items")[0].warehouse)
|
pi.get("items")[0].warehouse)
|
||||||
@ -950,6 +951,102 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
acc_settings.submit_journal_entriessubmit_journal_entries = 0
|
acc_settings.submit_journal_entriessubmit_journal_entries = 0
|
||||||
acc_settings.save()
|
acc_settings.save()
|
||||||
|
|
||||||
|
def test_purchase_invoice_advance_taxes(self):
|
||||||
|
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||||
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
|
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
|
||||||
|
|
||||||
|
# create a new supplier to test
|
||||||
|
supplier = create_supplier(supplier_name = '_Test TDS Advance Supplier',
|
||||||
|
tax_withholding_category = 'TDS - 194 - Dividends - Individual')
|
||||||
|
|
||||||
|
# Update tax withholding category with current fiscal year and rate details
|
||||||
|
update_tax_witholding_category('_Test Company', 'TDS Payable - _TC', nowdate())
|
||||||
|
|
||||||
|
# Create Purchase Order with TDS applied
|
||||||
|
po = create_purchase_order(do_not_save=1, supplier=supplier.name, rate=3000)
|
||||||
|
po.apply_tds = 1
|
||||||
|
po.tax_withholding_category = 'TDS - 194 - Dividends - Individual'
|
||||||
|
po.save()
|
||||||
|
po.submit()
|
||||||
|
|
||||||
|
# Update Unrealized Profit / Loss Account which is used as default advance tax account
|
||||||
|
frappe.db.set_value('Company', '_Test Company', 'unrealized_profit_loss_account', '_Test Account Excise Duty - _TC')
|
||||||
|
|
||||||
|
# Create Payment Entry Against the order
|
||||||
|
payment_entry = get_payment_entry(dt='Purchase Order', dn=po.name)
|
||||||
|
payment_entry.paid_from = 'Cash - _TC'
|
||||||
|
payment_entry.save()
|
||||||
|
payment_entry.submit()
|
||||||
|
|
||||||
|
# Check GLE for Payment Entry
|
||||||
|
expected_gle = [
|
||||||
|
['_Test Account Excise Duty - _TC', 3000, 0],
|
||||||
|
['Cash - _TC', 0, 27000],
|
||||||
|
['Creditors - _TC', 27000, 0],
|
||||||
|
['TDS Payable - _TC', 0, 3000],
|
||||||
|
]
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
|
from `tabGL Entry`
|
||||||
|
where voucher_type='Payment Entry' and voucher_no=%s
|
||||||
|
order by account asc""", (payment_entry.name), as_dict=1)
|
||||||
|
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEqual(expected_gle[i][0], gle.account)
|
||||||
|
self.assertEqual(expected_gle[i][1], gle.debit)
|
||||||
|
self.assertEqual(expected_gle[i][2], gle.credit)
|
||||||
|
|
||||||
|
# Create Purchase Invoice against Purchase Order
|
||||||
|
purchase_invoice = get_mapped_purchase_invoice(po.name)
|
||||||
|
purchase_invoice.allocate_advances_automatically = 1
|
||||||
|
purchase_invoice.items[0].expense_account = '_Test Account Cost for Goods Sold - _TC'
|
||||||
|
purchase_invoice.save()
|
||||||
|
purchase_invoice.submit()
|
||||||
|
|
||||||
|
# Check GLE for Purchase Invoice
|
||||||
|
# Zero net effect on final TDS Payable on invoice
|
||||||
|
expected_gle = [
|
||||||
|
['_Test Account Cost for Goods Sold - _TC', 30000, 0],
|
||||||
|
['_Test Account Excise Duty - _TC', 0, 3000],
|
||||||
|
['Creditors - _TC', 0, 27000],
|
||||||
|
['TDS Payable - _TC', 3000, 3000]
|
||||||
|
]
|
||||||
|
|
||||||
|
gl_entries = frappe.db.sql("""select account, debit, credit
|
||||||
|
from `tabGL Entry`
|
||||||
|
where voucher_type='Purchase Invoice' and voucher_no=%s
|
||||||
|
order by account asc""", (purchase_invoice.name), as_dict=1)
|
||||||
|
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEqual(expected_gle[i][0], gle.account)
|
||||||
|
self.assertEqual(expected_gle[i][1], gle.debit)
|
||||||
|
self.assertEqual(expected_gle[i][2], gle.credit)
|
||||||
|
|
||||||
|
def update_tax_witholding_category(company, account, date):
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
|
fiscal_year = get_fiscal_year(date=date, company=company)
|
||||||
|
|
||||||
|
if not frappe.db.get_value('Tax Withholding Rate',
|
||||||
|
{'parent': 'TDS - 194 - Dividends - Individual', 'fiscal_year': fiscal_year[0]}):
|
||||||
|
tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
|
||||||
|
tds_category.append('rates', {
|
||||||
|
'fiscal_year': fiscal_year[0],
|
||||||
|
'tax_withholding_rate': 10,
|
||||||
|
'single_threshold': 2500,
|
||||||
|
'cumulative_threshold': 0
|
||||||
|
})
|
||||||
|
tds_category.save()
|
||||||
|
|
||||||
|
if not frappe.db.get_value('Tax Withholding Account',
|
||||||
|
{'parent': 'TDS - 194 - Dividends - Individual', 'account': account}):
|
||||||
|
tds_category = frappe.get_doc('Tax Withholding Category', 'TDS - 194 - Dividends - Individual')
|
||||||
|
tds_category.append('accounts', {
|
||||||
|
'company': company,
|
||||||
|
'account': account
|
||||||
|
})
|
||||||
|
tds_category.save()
|
||||||
|
|
||||||
def unlink_payment_on_cancel_of_invoice(enable=1):
|
def unlink_payment_on_cancel_of_invoice(enable=1):
|
||||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||||
@ -994,7 +1091,8 @@ def make_purchase_invoice(**args):
|
|||||||
"project": args.project,
|
"project": args.project,
|
||||||
"rejected_warehouse": args.rejected_warehouse or "",
|
"rejected_warehouse": args.rejected_warehouse or "",
|
||||||
"rejected_serial_no": args.rejected_serial_no or "",
|
"rejected_serial_no": args.rejected_serial_no or "",
|
||||||
"asset_location": args.location or ""
|
"asset_location": args.location or "",
|
||||||
|
"allow_zero_valuation_rate": args.get("allow_zero_valuation_rate") or 0
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.get_taxes_and_charges:
|
if args.get_taxes_and_charges:
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"charge_type",
|
"charge_type",
|
||||||
"row_id",
|
"row_id",
|
||||||
"included_in_print_rate",
|
"included_in_print_rate",
|
||||||
|
"included_in_paid_amount",
|
||||||
"col_break1",
|
"col_break1",
|
||||||
"account_head",
|
"account_head",
|
||||||
"description",
|
"description",
|
||||||
@ -21,6 +22,7 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"section_break_9",
|
"section_break_9",
|
||||||
|
"currency",
|
||||||
"tax_amount",
|
"tax_amount",
|
||||||
"tax_amount_after_discount_amount",
|
"tax_amount_after_discount_amount",
|
||||||
"total",
|
"total",
|
||||||
@ -205,12 +207,28 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "account_head.account_currency",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Account Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||||
|
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
|
||||||
|
"fieldname": "included_in_paid_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Considered In Paid Amount"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-09-18 17:26:09.703215",
|
"modified": "2021-06-14 01:43:50.750455",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Taxes and Charges",
|
"name": "Purchase Taxes and Charges",
|
||||||
|
@ -17,7 +17,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
|||||||
var me = this;
|
var me = this;
|
||||||
this._super();
|
this._super();
|
||||||
|
|
||||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet'];
|
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', 'POS Closing Entry'];
|
||||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||||
// show debit_to in print format
|
// show debit_to in print format
|
||||||
this.frm.set_df_property("debit_to", "print_hide", 0);
|
this.frm.set_df_property("debit_to", "print_hide", 0);
|
||||||
|
@ -531,7 +531,7 @@ class SalesInvoice(SellingController):
|
|||||||
# set pos values in items
|
# set pos values in items
|
||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if item.get('item_code'):
|
if item.get('item_code'):
|
||||||
profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos)
|
profile_details = get_pos_profile_item_details(pos, frappe._dict(item.as_dict()), pos, update_data=True)
|
||||||
for fname, val in iteritems(profile_details):
|
for fname, val in iteritems(profile_details):
|
||||||
if (not for_validate) or (for_validate and not item.get(fname)):
|
if (not for_validate) or (for_validate and not item.get(fname)):
|
||||||
item.set(fname, val)
|
item.set(fname, val)
|
||||||
@ -842,6 +842,8 @@ class SalesInvoice(SellingController):
|
|||||||
self.make_tax_gl_entries(gl_entries)
|
self.make_tax_gl_entries(gl_entries)
|
||||||
self.make_internal_transfer_gl_entries(gl_entries)
|
self.make_internal_transfer_gl_entries(gl_entries)
|
||||||
|
|
||||||
|
self.allocate_advance_taxes(gl_entries)
|
||||||
|
|
||||||
self.make_item_gl_entries(gl_entries)
|
self.make_item_gl_entries(gl_entries)
|
||||||
|
|
||||||
# merge gl entries before adding pos entries
|
# merge gl entries before adding pos entries
|
||||||
@ -849,7 +851,6 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
self.make_loyalty_point_redemption_gle(gl_entries)
|
self.make_loyalty_point_redemption_gle(gl_entries)
|
||||||
self.make_pos_gl_entries(gl_entries)
|
self.make_pos_gl_entries(gl_entries)
|
||||||
self.make_gle_for_change_amount(gl_entries)
|
|
||||||
|
|
||||||
self.make_write_off_gl_entry(gl_entries)
|
self.make_write_off_gl_entry(gl_entries)
|
||||||
self.make_gle_for_rounding_adjustment(gl_entries)
|
self.make_gle_for_rounding_adjustment(gl_entries)
|
||||||
@ -983,7 +984,13 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def make_pos_gl_entries(self, gl_entries):
|
def make_pos_gl_entries(self, gl_entries):
|
||||||
if cint(self.is_pos):
|
if cint(self.is_pos):
|
||||||
|
|
||||||
|
skip_change_gl_entries = not cint(frappe.db.get_single_value('Accounts Settings', 'post_change_gl_entries'))
|
||||||
|
|
||||||
for payment_mode in self.payments:
|
for payment_mode in self.payments:
|
||||||
|
if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
|
||||||
|
payment_mode.base_amount -= self.change_amount
|
||||||
|
|
||||||
if payment_mode.amount:
|
if payment_mode.amount:
|
||||||
# POS, make payment entries
|
# POS, make payment entries
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
@ -1015,8 +1022,11 @@ class SalesInvoice(SellingController):
|
|||||||
}, payment_mode_account_currency, item=self)
|
}, payment_mode_account_currency, item=self)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not skip_change_gl_entries:
|
||||||
|
self.make_gle_for_change_amount(gl_entries)
|
||||||
|
|
||||||
def make_gle_for_change_amount(self, gl_entries):
|
def make_gle_for_change_amount(self, gl_entries):
|
||||||
if cint(self.is_pos) and self.change_amount:
|
if self.change_amount:
|
||||||
if self.account_for_change_amount:
|
if self.account_for_change_amount:
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
|
@ -713,7 +713,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.submit()
|
si.submit()
|
||||||
self.assertEqual(si.paid_amount, 100.0)
|
self.assertEqual(si.paid_amount, 100.0)
|
||||||
|
|
||||||
self.pos_gl_entry(si, pos, 50)
|
self.validate_pos_gl_entry(si, pos, 50)
|
||||||
|
|
||||||
def test_pos_returns_with_repayment(self):
|
def test_pos_returns_with_repayment(self):
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
|
||||||
@ -749,7 +749,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||||
|
|
||||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
||||||
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
|
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
|
||||||
|
|
||||||
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
|
||||||
@ -770,7 +770,45 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
self.assertEqual(pos.grand_total, 100.0)
|
self.assertEqual(pos.grand_total, 100.0)
|
||||||
self.assertEqual(pos.write_off_amount, -5)
|
self.assertEqual(pos.write_off_amount, -5)
|
||||||
|
|
||||||
def pos_gl_entry(self, si, pos, cash_amount):
|
def test_pos_with_no_gl_entry_for_change_amount(self):
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 0)
|
||||||
|
|
||||||
|
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||||
|
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||||
|
|
||||||
|
make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
||||||
|
item_code= "_Test FG Item",warehouse= "Stores - TCP1", cost_center= "Main - TCP1")
|
||||||
|
|
||||||
|
pos = create_sales_invoice(company= "_Test Company with perpetual inventory",
|
||||||
|
debit_to="Debtors - TCP1", item_code= "_Test FG Item", warehouse="Stores - TCP1",
|
||||||
|
income_account = "Sales - TCP1", expense_account = "Cost of Goods Sold - TCP1",
|
||||||
|
cost_center = "Main - TCP1", do_not_save=True)
|
||||||
|
|
||||||
|
pos.is_pos = 1
|
||||||
|
pos.update_stock = 1
|
||||||
|
|
||||||
|
taxes = get_taxes_and_charges()
|
||||||
|
pos.taxes = []
|
||||||
|
for tax in taxes:
|
||||||
|
pos.append("taxes", tax)
|
||||||
|
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 50})
|
||||||
|
pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - TCP1', 'amount': 60})
|
||||||
|
|
||||||
|
pos.insert()
|
||||||
|
pos.submit()
|
||||||
|
|
||||||
|
self.assertEqual(pos.grand_total, 100.0)
|
||||||
|
self.assertEqual(pos.change_amount, 10)
|
||||||
|
|
||||||
|
self.validate_pos_gl_entry(pos, pos, 60, validate_without_change_gle=True)
|
||||||
|
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'post_change_gl_entries', 1)
|
||||||
|
|
||||||
|
def validate_pos_gl_entry(self, si, pos, cash_amount, validate_without_change_gle=False):
|
||||||
|
if validate_without_change_gle:
|
||||||
|
cash_amount -= pos.change_amount
|
||||||
|
|
||||||
# check stock ledger entries
|
# check stock ledger entries
|
||||||
sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
|
sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
|
||||||
where voucher_type = 'Sales Invoice' and voucher_no = %s""",
|
where voucher_type = 'Sales Invoice' and voucher_no = %s""",
|
||||||
@ -1899,8 +1937,27 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
frappe.flags.country = country
|
frappe.flags.country = country
|
||||||
|
|
||||||
def test_einvoice_json(self):
|
def test_einvoice_json(self):
|
||||||
from erpnext.regional.india.e_invoice.utils import make_einvoice
|
from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
|
||||||
|
|
||||||
|
si = get_sales_invoice_for_e_invoice()
|
||||||
|
si.discount_amount = 100
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
einvoice = make_einvoice(si)
|
||||||
|
self.assertTrue(einvoice['EwbDtls'])
|
||||||
|
validate_totals(einvoice)
|
||||||
|
|
||||||
|
si.apply_discount_on = 'Net Total'
|
||||||
|
si.save()
|
||||||
|
einvoice = make_einvoice(si)
|
||||||
|
validate_totals(einvoice)
|
||||||
|
|
||||||
|
[d.set('included_in_print_rate', 1) for d in si.taxes]
|
||||||
|
si.save()
|
||||||
|
einvoice = make_einvoice(si)
|
||||||
|
validate_totals(einvoice)
|
||||||
|
|
||||||
|
def get_sales_invoice_for_e_invoice():
|
||||||
si = make_sales_invoice_for_ewaybill()
|
si = make_sales_invoice_for_ewaybill()
|
||||||
si.naming_series = 'INV-2020-.#####'
|
si.naming_series = 'INV-2020-.#####'
|
||||||
si.items = []
|
si.items = []
|
||||||
@ -1914,6 +1971,7 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"expense_account": "Cost of Goods Sold - _TC",
|
"expense_account": "Cost of Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
})
|
})
|
||||||
|
|
||||||
si.append("items", {
|
si.append("items", {
|
||||||
"item_code": "_Test Item 2",
|
"item_code": "_Test Item 2",
|
||||||
"uom": "Nos",
|
"uom": "Nos",
|
||||||
@ -1924,44 +1982,35 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
"expense_account": "Cost of Goods Sold - _TC",
|
"expense_account": "Cost of Goods Sold - _TC",
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
})
|
})
|
||||||
si.discount_amount = 100
|
|
||||||
si.save()
|
|
||||||
|
|
||||||
einvoice = make_einvoice(si)
|
return si
|
||||||
|
|
||||||
total_item_ass_value = 0
|
def test_item_tax_net_range(self):
|
||||||
total_item_cgst_value = 0
|
item = create_item("T Shirt")
|
||||||
total_item_sgst_value = 0
|
|
||||||
total_item_igst_value = 0
|
|
||||||
total_item_value = 0
|
|
||||||
|
|
||||||
for item in einvoice['ItemList']:
|
item.set('taxes', [])
|
||||||
total_item_ass_value += item['AssAmt']
|
item.append("taxes", {
|
||||||
total_item_cgst_value += item['CgstAmt']
|
"item_tax_template": "_Test Account Excise Duty @ 10 - _TC",
|
||||||
total_item_sgst_value += item['SgstAmt']
|
"minimum_net_rate": 0,
|
||||||
total_item_igst_value += item['IgstAmt']
|
"maximum_net_rate": 500
|
||||||
total_item_value += item['TotItemVal']
|
})
|
||||||
|
|
||||||
self.assertTrue(item['AssAmt'], item['TotAmt'] - item['Discount'])
|
item.append("taxes", {
|
||||||
self.assertTrue(item['TotItemVal'], item['AssAmt'] + item['CgstAmt'] + item['SgstAmt'] + item['IgstAmt'])
|
"item_tax_template": "_Test Account Excise Duty @ 12 - _TC",
|
||||||
|
"minimum_net_rate": 501,
|
||||||
|
"maximum_net_rate": 1000
|
||||||
|
})
|
||||||
|
|
||||||
value_details = einvoice['ValDtls']
|
item.save()
|
||||||
|
|
||||||
self.assertEqual(einvoice['Version'], '1.1')
|
sales_invoice = create_sales_invoice(item = "T Shirt", rate=700, do_not_submit=True)
|
||||||
self.assertEqual(value_details['AssVal'], total_item_ass_value)
|
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
|
||||||
self.assertEqual(value_details['CgstVal'], total_item_cgst_value)
|
|
||||||
self.assertEqual(value_details['SgstVal'], total_item_sgst_value)
|
|
||||||
self.assertEqual(value_details['IgstVal'], total_item_igst_value)
|
|
||||||
|
|
||||||
calculated_invoice_value = \
|
# Apply discount
|
||||||
value_details['AssVal'] + value_details['CgstVal'] \
|
sales_invoice.apply_discount_on = 'Net Total'
|
||||||
+ value_details['SgstVal'] + value_details['IgstVal'] \
|
sales_invoice.discount_amount = 300
|
||||||
+ value_details['OthChrg'] - value_details['Discount']
|
sales_invoice.save()
|
||||||
|
self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
|
||||||
self.assertTrue(value_details['TotInvVal'] - calculated_invoice_value < 0.1)
|
|
||||||
|
|
||||||
self.assertEqual(value_details['TotInvVal'], si.base_grand_total)
|
|
||||||
self.assertTrue(einvoice['EwbDtls'])
|
|
||||||
|
|
||||||
def make_test_address_for_ewaybill():
|
def make_test_address_for_ewaybill():
|
||||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||||
@ -2085,27 +2134,6 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
|
|||||||
doc.assertEqual(expected_gle[i][2], gle.credit)
|
doc.assertEqual(expected_gle[i][2], gle.credit)
|
||||||
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
||||||
|
|
||||||
def test_item_tax_validity(self):
|
|
||||||
item = frappe.get_doc("Item", "_Test Item 2")
|
|
||||||
|
|
||||||
if item.taxes:
|
|
||||||
item.taxes = []
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
item.append("taxes", {
|
|
||||||
"item_tax_template": "_Test Item Tax Template 1 - _TC",
|
|
||||||
"valid_from": add_days(nowdate(), 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1)
|
|
||||||
sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1 - _TC"
|
|
||||||
self.assertRaises(frappe.ValidationError, sales_invoice.save)
|
|
||||||
|
|
||||||
item.taxes = []
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
def create_sales_invoice(**args):
|
def create_sales_invoice(**args):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2013-04-24 11:39:32",
|
"creation": "2013-04-24 11:39:32",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Setup",
|
"document_type": "Setup",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"charge_type",
|
"charge_type",
|
||||||
"row_id",
|
"row_id",
|
||||||
@ -10,12 +12,14 @@
|
|||||||
"col_break_1",
|
"col_break_1",
|
||||||
"description",
|
"description",
|
||||||
"included_in_print_rate",
|
"included_in_print_rate",
|
||||||
|
"included_in_paid_amount",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"section_break_8",
|
"section_break_8",
|
||||||
"rate",
|
"rate",
|
||||||
"section_break_9",
|
"section_break_9",
|
||||||
|
"currency",
|
||||||
"tax_amount",
|
"tax_amount",
|
||||||
"total",
|
"total",
|
||||||
"tax_amount_after_discount_amount",
|
"tax_amount_after_discount_amount",
|
||||||
@ -23,8 +27,7 @@
|
|||||||
"base_tax_amount",
|
"base_tax_amount",
|
||||||
"base_total",
|
"base_total",
|
||||||
"base_tax_amount_after_discount_amount",
|
"base_tax_amount_after_discount_amount",
|
||||||
"item_wise_tax_detail",
|
"item_wise_tax_detail"
|
||||||
"parenttype"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -173,17 +176,6 @@
|
|||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "parenttype",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"hidden": 1,
|
|
||||||
"in_filter": 1,
|
|
||||||
"label": "Parenttype",
|
|
||||||
"oldfieldname": "parenttype",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"print_hide": 1,
|
|
||||||
"search_index": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "accounting_dimensions_section",
|
"fieldname": "accounting_dimensions_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -192,15 +184,34 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "account_head.account_currency",
|
||||||
|
"fieldname": "currency",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Account Currency",
|
||||||
|
"options": "Currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||||
|
"description": "If checked, the tax amount will be considered as already included in the Paid Amount in Payment Entry",
|
||||||
|
"fieldname": "included_in_paid_amount",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Considered In Paid Amount"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-25 22:59:38.740883",
|
"links": [],
|
||||||
|
"modified": "2021-06-14 01:44:36.899147",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Taxes and Charges",
|
"name": "Sales Taxes and Charges",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC"
|
"sort_order": "ASC"
|
||||||
}
|
}
|
@ -49,7 +49,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
|||||||
if not parties:
|
if not parties:
|
||||||
parties.append(party)
|
parties.append(party)
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(inv.posting_date, company=inv.company)
|
fiscal_year = get_fiscal_year(inv.get('posting_date') or inv.get('transaction_date'), company=inv.company)
|
||||||
tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
|
tax_details = get_tax_withholding_details(tax_withholding_category, fiscal_year[0], inv.company)
|
||||||
|
|
||||||
if not tax_details:
|
if not tax_details:
|
||||||
@ -154,7 +154,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, p
|
|||||||
tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
|
tax_deducted = get_deducted_tax(taxable_vouchers, fiscal_year, tax_details)
|
||||||
|
|
||||||
tax_amount = 0
|
tax_amount = 0
|
||||||
posting_date = inv.posting_date
|
posting_date = inv.get('posting_date') or inv.get('transaction_date')
|
||||||
if party_type == 'Supplier':
|
if party_type == 'Supplier':
|
||||||
ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
|
ldc = get_lower_deduction_certificate(fiscal_year, pan_no)
|
||||||
if tax_deducted:
|
if tax_deducted:
|
||||||
@ -257,7 +257,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu
|
|||||||
if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
|
if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)):
|
||||||
if ldc and is_valid_certificate(
|
if ldc and is_valid_certificate(
|
||||||
ldc.valid_from, ldc.valid_upto,
|
ldc.valid_from, ldc.valid_upto,
|
||||||
inv.posting_date, tax_deducted,
|
inv.get('posting_date') or inv.get('transaction_date'), tax_deducted,
|
||||||
inv.net_total, ldc.certificate_limit
|
inv.net_total, ldc.certificate_limit
|
||||||
):
|
):
|
||||||
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
|
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
|
||||||
|
@ -185,10 +185,10 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
|
|||||||
for d in gl_map:
|
for d in gl_map:
|
||||||
if d.account == round_off_account:
|
if d.account == round_off_account:
|
||||||
round_off_gle = d
|
round_off_gle = d
|
||||||
if d.debit_in_account_currency:
|
if d.debit:
|
||||||
debit_credit_diff -= flt(d.debit_in_account_currency)
|
debit_credit_diff -= flt(d.debit)
|
||||||
else:
|
else:
|
||||||
debit_credit_diff += flt(d.credit_in_account_currency)
|
debit_credit_diff += flt(d.credit)
|
||||||
round_off_account_exists = True
|
round_off_account_exists = True
|
||||||
|
|
||||||
if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
|
if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)):
|
||||||
|
@ -457,7 +457,7 @@ def validate_party_frozen_disabled(party_type, party_name):
|
|||||||
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
|
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
|
||||||
|
|
||||||
elif party_type == "Employee":
|
elif party_type == "Employee":
|
||||||
if frappe.db.get_value("Employee", party_name, "status") == "Left":
|
if frappe.db.get_value("Employee", party_name, "status") != "Active":
|
||||||
frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True)
|
frappe.msgprint(_("{0} {1} is not active").format(party_type, party_name), alert=True)
|
||||||
|
|
||||||
def get_timeline_data(doctype, name):
|
def get_timeline_data(doctype, name):
|
||||||
|
@ -58,11 +58,9 @@ def get_conditions(filters):
|
|||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
|
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
|
|
||||||
accounts = frappe.db.get_all("Account", fields=["name", "account_currency"],
|
accounts = frappe.db.get_all("Account", fields=["name", "account_currency"],
|
||||||
filters=conditions)
|
filters=conditions, order_by='name')
|
||||||
|
|
||||||
for d in accounts:
|
for d in accounts:
|
||||||
balance = get_balance_on(d.name, date=filters.report_date)
|
balance = get_balance_on(d.name, date=filters.report_date)
|
||||||
|
@ -23,7 +23,7 @@ class TestAccountBalance(unittest.TestCase):
|
|||||||
|
|
||||||
expected_data = [
|
expected_data = [
|
||||||
{
|
{
|
||||||
"account": 'Sales - _TC2',
|
"account": 'Direct Income - _TC2',
|
||||||
"currency": 'EUR',
|
"currency": 'EUR',
|
||||||
"balance": -100.0,
|
"balance": -100.0,
|
||||||
},
|
},
|
||||||
@ -32,21 +32,21 @@ class TestAccountBalance(unittest.TestCase):
|
|||||||
"currency": 'EUR',
|
"currency": 'EUR',
|
||||||
"balance": -100.0,
|
"balance": -100.0,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"account": 'Service - _TC2',
|
|
||||||
"currency": 'EUR',
|
|
||||||
"balance": 0.0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"account": 'Direct Income - _TC2',
|
|
||||||
"currency": 'EUR',
|
|
||||||
"balance": -100.0,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"account": 'Indirect Income - _TC2',
|
"account": 'Indirect Income - _TC2',
|
||||||
"currency": 'EUR',
|
"currency": 'EUR',
|
||||||
"balance": 0.0,
|
"balance": 0.0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"account": 'Sales - _TC2',
|
||||||
|
"currency": 'EUR',
|
||||||
|
"balance": -100.0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"account": 'Service - _TC2',
|
||||||
|
"currency": 'EUR',
|
||||||
|
"balance": 0.0,
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
self.assertEqual(expected_data, report[1])
|
self.assertEqual(expected_data, report[1])
|
||||||
|
@ -584,6 +584,7 @@ class ReceivablePayableReport(object):
|
|||||||
`tabGL Entry`
|
`tabGL Entry`
|
||||||
where
|
where
|
||||||
docstatus < 2
|
docstatus < 2
|
||||||
|
and is_cancelled = 0
|
||||||
and party_type=%s
|
and party_type=%s
|
||||||
and (party is not null and party != '')
|
and (party is not null and party != '')
|
||||||
{1} {2} {3}"""
|
{1} {2} {3}"""
|
||||||
|
@ -54,6 +54,32 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
|||||||
frappe.query_report.refresh();
|
frappe.query_report.refresh();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"fieldname":"purchase_order",
|
||||||
|
"label": __("Purchase Order"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Purchase Order",
|
||||||
|
"get_query": function() {
|
||||||
|
return {
|
||||||
|
"filters": {
|
||||||
|
"name": ["in", frappe.query_report.invoices]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
on_change: function() {
|
||||||
|
let supplier = frappe.query_report.get_filter_value('supplier');
|
||||||
|
if(!supplier) return; // return if no supplier selected
|
||||||
|
|
||||||
|
// filter invoices based on selected supplier
|
||||||
|
let invoices = [];
|
||||||
|
frappe.query_report.invoice_data.map(d => {
|
||||||
|
if(d.supplier==supplier)
|
||||||
|
invoices.push(d.name)
|
||||||
|
});
|
||||||
|
frappe.query_report.invoices = invoices;
|
||||||
|
frappe.query_report.refresh();
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname":"from_date",
|
"fieldname":"from_date",
|
||||||
"label": __("From Date"),
|
"label": __("From Date"),
|
||||||
@ -75,15 +101,17 @@ frappe.query_reports["TDS Payable Monthly"] = {
|
|||||||
onload: function(report) {
|
onload: function(report) {
|
||||||
// fetch all tds applied invoices
|
// fetch all tds applied invoices
|
||||||
frappe.call({
|
frappe.call({
|
||||||
"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices",
|
"method": "erpnext.accounts.report.tds_payable_monthly.tds_payable_monthly.get_tds_invoices_and_orders",
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
let invoices = [];
|
let invoices = [];
|
||||||
|
|
||||||
r.message.map(d => {
|
r.message.map(d => {
|
||||||
invoices.push(d.name);
|
invoices.push(d.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
report["invoice_data"] = r.message;
|
report["invoice_data"] = r.message.invoices;
|
||||||
report["invoices"] = invoices;
|
report["invoices"] = invoices;
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,14 @@ def execute(filters=None):
|
|||||||
validate_filters(filters)
|
validate_filters(filters)
|
||||||
set_filters(filters)
|
set_filters(filters)
|
||||||
|
|
||||||
|
# TDS payment entries
|
||||||
|
payment_entries = get_payment_entires(filters)
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
if not filters["invoices"]:
|
if not filters.get("invoices"):
|
||||||
return columns, []
|
return columns, []
|
||||||
|
|
||||||
res = get_result(filters)
|
res = get_result(filters, payment_entries)
|
||||||
|
|
||||||
return columns, res
|
return columns, res
|
||||||
|
|
||||||
@ -27,8 +30,9 @@ def validate_filters(filters):
|
|||||||
def set_filters(filters):
|
def set_filters(filters):
|
||||||
invoices = []
|
invoices = []
|
||||||
|
|
||||||
if not filters["invoices"]:
|
if not filters.get("invoices"):
|
||||||
filters["invoices"] = get_tds_invoices()
|
filters["invoices"] = get_tds_invoices_and_orders()
|
||||||
|
|
||||||
if filters.supplier and filters.purchase_invoice:
|
if filters.supplier and filters.purchase_invoice:
|
||||||
for d in filters["invoices"]:
|
for d in filters["invoices"]:
|
||||||
if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
|
if d.name == filters.purchase_invoice and d.supplier == filters.supplier:
|
||||||
@ -41,13 +45,29 @@ def set_filters(filters):
|
|||||||
for d in filters["invoices"]:
|
for d in filters["invoices"]:
|
||||||
if d.name == filters.purchase_invoice:
|
if d.name == filters.purchase_invoice:
|
||||||
invoices.append(d)
|
invoices.append(d)
|
||||||
|
elif filters.supplier and filters.purchase_order:
|
||||||
|
for d in filters.get("invoices"):
|
||||||
|
if d.name == filters.purchase_order and d.supplier == filters.supplier:
|
||||||
|
invoices.append(d)
|
||||||
|
elif filters.supplier and not filters.purchase_order:
|
||||||
|
for d in filters.get("invoices"):
|
||||||
|
if d.supplier == filters.supplier:
|
||||||
|
invoices.append(d)
|
||||||
|
elif filters.purchase_order and not filters.supplier:
|
||||||
|
for d in filters.get("invoices"):
|
||||||
|
if d.name == filters.purchase_order:
|
||||||
|
invoices.append(d)
|
||||||
|
|
||||||
filters["invoices"] = invoices if invoices else filters["invoices"]
|
filters["invoices"] = invoices if invoices else filters["invoices"]
|
||||||
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
filters.naming_series = frappe.db.get_single_value('Buying Settings', 'supp_master_name')
|
||||||
|
|
||||||
def get_result(filters):
|
#print(filters.get('invoices'))
|
||||||
supplier_map, tds_docs = get_supplier_map(filters)
|
|
||||||
gle_map = get_gle_map(filters)
|
def get_result(filters, payment_entries):
|
||||||
|
supplier_map, tds_docs = get_supplier_map(filters, payment_entries)
|
||||||
|
documents = [d.get('name') for d in filters.get('invoices')] + [d.get('name') for d in payment_entries]
|
||||||
|
|
||||||
|
gle_map = get_gle_map(filters, documents)
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
for d in gle_map:
|
for d in gle_map:
|
||||||
@ -62,10 +82,11 @@ def get_result(filters):
|
|||||||
|
|
||||||
for k in gle_map[d]:
|
for k in gle_map[d]:
|
||||||
if k.party == supplier_map[d] and k.credit > 0:
|
if k.party == supplier_map[d] and k.credit > 0:
|
||||||
total_amount_credited += k.credit
|
total_amount_credited += (k.credit - k.debit)
|
||||||
elif account_list and k.account == account and k.credit > 0:
|
elif account_list and k.account == account and (k.credit - k.debit) > 0:
|
||||||
tds_deducted = k.credit
|
tds_deducted = (k.credit - k.debit)
|
||||||
total_amount_credited += k.credit
|
total_amount_credited += (k.credit - k.debit)
|
||||||
|
voucher_type = k.voucher_type
|
||||||
|
|
||||||
rate = [i.tax_withholding_rate for i in tds_doc.rates
|
rate = [i.tax_withholding_rate for i in tds_doc.rates
|
||||||
if i.fiscal_year == gle_map[d][0].fiscal_year]
|
if i.fiscal_year == gle_map[d][0].fiscal_year]
|
||||||
@ -73,32 +94,36 @@ def get_result(filters):
|
|||||||
if rate and len(rate) > 0 and tds_deducted:
|
if rate and len(rate) > 0 and tds_deducted:
|
||||||
rate = rate[0]
|
rate = rate[0]
|
||||||
|
|
||||||
if getdate(filters.from_date) <= gle_map[d][0].posting_date \
|
|
||||||
and getdate(filters.to_date) >= gle_map[d][0].posting_date:
|
|
||||||
row = [supplier.pan, supplier.name]
|
row = [supplier.pan, supplier.name]
|
||||||
|
|
||||||
if filters.naming_series == 'Naming Series':
|
if filters.naming_series == 'Naming Series':
|
||||||
row.append(supplier.supplier_name)
|
row.append(supplier.supplier_name)
|
||||||
|
|
||||||
row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
|
row.extend([tds_doc.name, supplier.supplier_type, rate, total_amount_credited,
|
||||||
tds_deducted, gle_map[d][0].posting_date, "Purchase Invoice", d])
|
tds_deducted, gle_map[d][0].posting_date, voucher_type, d])
|
||||||
out.append(row)
|
out.append(row)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|
||||||
def get_supplier_map(filters):
|
def get_supplier_map(filters, payment_entries):
|
||||||
# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
|
# create a supplier_map of the form {"purchase_invoice": {supplier_name, pan, tds_name}}
|
||||||
# pre-fetch all distinct applicable tds docs
|
# pre-fetch all distinct applicable tds docs
|
||||||
supplier_map, tds_docs = {}, {}
|
supplier_map, tds_docs = {}, {}
|
||||||
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
pan = "pan" if frappe.db.has_column("Supplier", "pan") else "tax_id"
|
||||||
|
supplier_list = [d.supplier for d in filters["invoices"]]
|
||||||
|
|
||||||
supplier_detail = frappe.db.get_all('Supplier',
|
supplier_detail = frappe.db.get_all('Supplier',
|
||||||
{"name": ["in", [d.supplier for d in filters["invoices"]]]},
|
{"name": ["in", supplier_list]},
|
||||||
["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
|
["tax_withholding_category", "name", pan+" as pan", "supplier_type", "supplier_name"])
|
||||||
|
|
||||||
for d in filters["invoices"]:
|
for d in filters["invoices"]:
|
||||||
supplier_map[d.get("name")] = [k for k in supplier_detail
|
supplier_map[d.get("name")] = [k for k in supplier_detail
|
||||||
if k.name == d.get("supplier")][0]
|
if k.name == d.get("supplier")][0]
|
||||||
|
|
||||||
|
for d in payment_entries:
|
||||||
|
supplier_map[d.get("name")] = [k for k in supplier_detail
|
||||||
|
if k.name == d.get("supplier")][0]
|
||||||
|
|
||||||
for d in supplier_detail:
|
for d in supplier_detail:
|
||||||
if d.get("tax_withholding_category") not in tds_docs:
|
if d.get("tax_withholding_category") not in tds_docs:
|
||||||
tds_docs[d.get("tax_withholding_category")] = \
|
tds_docs[d.get("tax_withholding_category")] = \
|
||||||
@ -106,13 +131,19 @@ def get_supplier_map(filters):
|
|||||||
|
|
||||||
return supplier_map, tds_docs
|
return supplier_map, tds_docs
|
||||||
|
|
||||||
def get_gle_map(filters):
|
def get_gle_map(filters, documents):
|
||||||
# create gle_map of the form
|
# create gle_map of the form
|
||||||
# {"purchase_invoice": list of dict of all gle created for this invoice}
|
# {"purchase_invoice": list of dict of all gle created for this invoice}
|
||||||
gle_map = {}
|
gle_map = {}
|
||||||
gle = frappe.db.get_all('GL Entry',\
|
|
||||||
{"voucher_no": ["in", [d.get("name") for d in filters["invoices"]]], 'is_cancelled': 0},
|
gle = frappe.db.get_all('GL Entry',
|
||||||
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date"])
|
{
|
||||||
|
"voucher_no": ["in", documents],
|
||||||
|
'is_cancelled': 0,
|
||||||
|
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
||||||
|
},
|
||||||
|
["fiscal_year", "credit", "debit", "account", "voucher_no", "posting_date", "voucher_type"],
|
||||||
|
)
|
||||||
|
|
||||||
for d in gle:
|
for d in gle:
|
||||||
if not d.voucher_no in gle_map:
|
if not d.voucher_no in gle_map:
|
||||||
@ -201,8 +232,26 @@ def get_columns(filters):
|
|||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
def get_payment_entires(filters):
|
||||||
|
filter_dict = {
|
||||||
|
'posting_date': ("between", [filters.get('from_date'), filters.get('to_date')]),
|
||||||
|
'party_type': 'Supplier',
|
||||||
|
'apply_tax_withholding_amount': 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if filters.get('purchase_invoice') or filters.get('purchase_order'):
|
||||||
|
parent = frappe.db.get_all('Payment Entry Reference',
|
||||||
|
{'reference_name': ('in', [d.get('name') for d in filters.get('invoices')])}, ['parent'])
|
||||||
|
|
||||||
|
filter_dict.update({'name': ('in', [d.get('parent') for d in parent])})
|
||||||
|
|
||||||
|
payment_entries = frappe.get_all('Payment Entry', fields=['name', 'party_name as supplier'],
|
||||||
|
filters=filter_dict)
|
||||||
|
|
||||||
|
return payment_entries
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tds_invoices():
|
def get_tds_invoices_and_orders():
|
||||||
# fetch tds applicable supplier and fetch invoices for these suppliers
|
# fetch tds applicable supplier and fetch invoices for these suppliers
|
||||||
suppliers = [d.name for d in frappe.db.get_list("Supplier",
|
suppliers = [d.name for d in frappe.db.get_list("Supplier",
|
||||||
{"tax_withholding_category": ["!=", ""]}, ["name"])]
|
{"tax_withholding_category": ["!=", ""]}, ["name"])]
|
||||||
@ -210,7 +259,12 @@ def get_tds_invoices():
|
|||||||
invoices = frappe.db.get_list("Purchase Invoice",
|
invoices = frappe.db.get_list("Purchase Invoice",
|
||||||
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
||||||
|
|
||||||
|
orders = frappe.db.get_list("Purchase Order",
|
||||||
|
{"supplier": ["in", suppliers]}, ["name", "supplier"])
|
||||||
|
|
||||||
|
invoices = invoices + orders
|
||||||
invoices = [d for d in invoices if d.supplier]
|
invoices = [d for d in invoices if d.supplier]
|
||||||
|
|
||||||
frappe.cache().hset("invoices", frappe.session.user, invoices)
|
frappe.cache().hset("invoices", frappe.session.user, invoices)
|
||||||
|
|
||||||
return invoices
|
return invoices
|
||||||
|
@ -684,6 +684,7 @@
|
|||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Goods and Services Tax (GST India)",
|
"label": "Goods and Services Tax (GST India)",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -694,6 +695,7 @@
|
|||||||
"link_to": "GST Settings",
|
"link_to": "GST Settings",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -704,6 +706,7 @@
|
|||||||
"link_to": "GST HSN Code",
|
"link_to": "GST HSN Code",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -714,6 +717,7 @@
|
|||||||
"link_to": "GSTR-1",
|
"link_to": "GSTR-1",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -724,6 +728,7 @@
|
|||||||
"link_to": "GSTR-2",
|
"link_to": "GSTR-2",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -734,6 +739,7 @@
|
|||||||
"link_to": "GSTR 3B Report",
|
"link_to": "GSTR 3B Report",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -744,6 +750,7 @@
|
|||||||
"link_to": "GST Sales Register",
|
"link_to": "GST Sales Register",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -754,6 +761,7 @@
|
|||||||
"link_to": "GST Purchase Register",
|
"link_to": "GST Purchase Register",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -764,6 +772,7 @@
|
|||||||
"link_to": "GST Itemised Sales Register",
|
"link_to": "GST Itemised Sales Register",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -774,6 +783,7 @@
|
|||||||
"link_to": "GST Itemised Purchase Register",
|
"link_to": "GST Itemised Purchase Register",
|
||||||
"link_type": "Report",
|
"link_type": "Report",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -784,6 +794,7 @@
|
|||||||
"link_to": "C-Form",
|
"link_to": "C-Form",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -794,6 +805,7 @@
|
|||||||
"link_to": "Lower Deduction Certificate",
|
"link_to": "Lower Deduction Certificate",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
|
"only_for": "India",
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1052,7 +1064,7 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-05-12 11:48:01.905144",
|
"modified": "2021-06-10 03:17:31.427945",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounting",
|
"name": "Accounting",
|
||||||
|
@ -45,6 +45,14 @@ frappe.ui.form.on("Purchase Order", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
||||||
|
},
|
||||||
|
|
||||||
|
apply_tds: function(frm) {
|
||||||
|
if (!frm.doc.apply_tds) {
|
||||||
|
frm.set_value("tax_withholding_category", '');
|
||||||
|
} else {
|
||||||
|
frm.set_value("tax_withholding_category", frm.supplier_tds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -313,7 +321,8 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
if(me.values) {
|
if(me.values) {
|
||||||
me.values.sub_con_rm_items.map((row,i) => {
|
me.values.sub_con_rm_items.map((row,i) => {
|
||||||
if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
|
if (!row.item_code || !row.rm_item_code || !row.warehouse || !row.qty || row.qty === 0) {
|
||||||
frappe.throw(__("Item Code, warehouse, quantity are required on row {0}", [i+1]));
|
let row_id = i+1;
|
||||||
|
frappe.throw(__("Item Code, warehouse and quantity are required on row {0}", [row_id]));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children())
|
me._make_rm_stock_entry(me.dialog.fields_dict.sub_con_rm_items.grid.get_selected_children())
|
||||||
@ -509,7 +518,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
|||||||
args: {
|
args: {
|
||||||
reference_doctype: me.frm.doctype,
|
reference_doctype: me.frm.doctype,
|
||||||
reference_name: me.frm.docname,
|
reference_name: me.frm.docname,
|
||||||
content: __('Reason for hold: ')+data.reason_for_hold,
|
content: __('Reason for hold:') + " " +data.reason_for_hold,
|
||||||
comment_email: frappe.session.user,
|
comment_email: frappe.session.user,
|
||||||
comment_by: frappe.session.user_fullname
|
comment_by: frappe.session.user_fullname
|
||||||
},
|
},
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@ from erpnext.accounts.party import get_party_account_currency
|
|||||||
from six import string_types
|
from six import string_types
|
||||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||||
|
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
|
||||||
unlink_inter_company_doc
|
unlink_inter_company_doc
|
||||||
|
|
||||||
@ -39,11 +40,18 @@ class PurchaseOrder(BuyingController):
|
|||||||
'percent_join_field': 'material_request'
|
'percent_join_field': 'material_request'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
def onload(self):
|
||||||
|
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||||
|
self.set_onload("supplier_tds", supplier_tds)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(PurchaseOrder, self).validate()
|
super(PurchaseOrder, self).validate()
|
||||||
|
|
||||||
self.set_status()
|
self.set_status()
|
||||||
|
|
||||||
|
# apply tax withholding only if checked and applicable
|
||||||
|
self.set_tax_withholding()
|
||||||
|
|
||||||
self.validate_supplier()
|
self.validate_supplier()
|
||||||
self.validate_schedule_date()
|
self.validate_schedule_date()
|
||||||
validate_for_items(self)
|
validate_for_items(self)
|
||||||
@ -87,6 +95,33 @@ class PurchaseOrder(BuyingController):
|
|||||||
if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
|
if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
|
||||||
self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
|
self.validate_rate_with_reference_doc([["Supplier Quotation", "supplier_quotation", "supplier_quotation_item"]])
|
||||||
|
|
||||||
|
def set_tax_withholding(self):
|
||||||
|
if not self.apply_tds:
|
||||||
|
return
|
||||||
|
|
||||||
|
tax_withholding_details = get_party_tax_withholding_details(self, self.tax_withholding_category)
|
||||||
|
|
||||||
|
if not tax_withholding_details:
|
||||||
|
return
|
||||||
|
|
||||||
|
accounts = []
|
||||||
|
for d in self.taxes:
|
||||||
|
if d.account_head == tax_withholding_details.get("account_head"):
|
||||||
|
d.update(tax_withholding_details)
|
||||||
|
accounts.append(d.account_head)
|
||||||
|
|
||||||
|
if not accounts or tax_withholding_details.get("account_head") not in accounts:
|
||||||
|
self.append("taxes", tax_withholding_details)
|
||||||
|
|
||||||
|
to_remove = [d for d in self.taxes
|
||||||
|
if not d.tax_amount and d.account_head == tax_withholding_details.get("account_head")]
|
||||||
|
|
||||||
|
for d in to_remove:
|
||||||
|
self.remove(d)
|
||||||
|
|
||||||
|
# calculate totals again after applying TDS
|
||||||
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
def validate_supplier(self):
|
def validate_supplier(self):
|
||||||
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
|
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
|
||||||
if prevent_po:
|
if prevent_po:
|
||||||
|
@ -1111,7 +1111,7 @@ def create_purchase_order(**args):
|
|||||||
|
|
||||||
po.schedule_date = add_days(nowdate(), 1)
|
po.schedule_date = add_days(nowdate(), 1)
|
||||||
po.company = args.company or "_Test Company"
|
po.company = args.company or "_Test Company"
|
||||||
po.supplier = args.customer or "_Test Supplier"
|
po.supplier = args.supplier or "_Test Supplier"
|
||||||
po.is_subcontracted = args.is_subcontracted or "No"
|
po.is_subcontracted = args.is_subcontracted or "No"
|
||||||
po.currency = args.currency or frappe.get_cached_value('Company', po.company, "default_currency")
|
po.currency = args.currency or frappe.get_cached_value('Company', po.company, "default_currency")
|
||||||
po.conversion_factor = args.conversion_factor or 1
|
po.conversion_factor = args.conversion_factor or 1
|
||||||
|
@ -576,6 +576,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.disable_rounded_total",
|
||||||
"fieldname": "base_rounding_adjustment",
|
"fieldname": "base_rounding_adjustment",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rounding Adjustment (Company Currency",
|
"label": "Rounding Adjustment (Company Currency",
|
||||||
@ -620,6 +621,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.disable_rounded_total",
|
||||||
"fieldname": "rounding_adjustment",
|
"fieldname": "rounding_adjustment",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rounding Adjustment",
|
"label": "Rounding Adjustment",
|
||||||
@ -802,7 +804,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-12-03 15:18:29.073368",
|
"modified": "2021-04-19 00:58:20.995491",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Supplier Quotation",
|
"name": "Supplier Quotation",
|
||||||
|
@ -9,12 +9,12 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
|||||||
from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute
|
from erpnext.buying.report.subcontracted_raw_materials_to_be_transferred.subcontracted_raw_materials_to_be_transferred import execute
|
||||||
import json, frappe, unittest
|
import json, frappe, unittest
|
||||||
|
|
||||||
class TestSubcontractedItemToBeReceived(unittest.TestCase):
|
class TestSubcontractedItemToBeTransferred(unittest.TestCase):
|
||||||
|
|
||||||
def test_pending_and_received_qty(self):
|
def test_pending_and_transferred_qty(self):
|
||||||
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
|
po = create_purchase_order(item_code='_Test FG Item', is_subcontracted='Yes')
|
||||||
make_stock_entry(item_code='_Test Item', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
make_stock_entry(item_code='_Test Item', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
|
||||||
make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse 1 - _TC', qty=100, basic_rate=100)
|
make_stock_entry(item_code='_Test Item Home Desktop 100', target='_Test Warehouse - _TC', qty=100, basic_rate=100)
|
||||||
transfer_subcontracted_raw_materials(po.name)
|
transfer_subcontracted_raw_materials(po.name)
|
||||||
col, data = execute(filters=frappe._dict({'supplier': po.supplier,
|
col, data = execute(filters=frappe._dict({'supplier': po.supplier,
|
||||||
'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
|
'from_date': frappe.utils.get_datetime(frappe.utils.add_to_date(po.transaction_date, days=-10)),
|
||||||
@ -38,6 +38,7 @@ def transfer_subcontracted_raw_materials(po):
|
|||||||
'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}]
|
'warehouse': '_Test Warehouse - _TC', 'rate': 100, 'amount': 200, 'stock_uom': 'Nos'}]
|
||||||
rm_item_string = json.dumps(rm_item)
|
rm_item_string = json.dumps(rm_item)
|
||||||
se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string))
|
se = frappe.get_doc(make_rm_stock_entry(po, rm_item_string))
|
||||||
|
se.from_warehouse = '_Test Warehouse 1 - _TC'
|
||||||
se.to_warehouse = '_Test Warehouse 1 - _TC'
|
se.to_warehouse = '_Test Warehouse 1 - _TC'
|
||||||
se.stock_entry_type = 'Send to Subcontractor'
|
se.stock_entry_type = 'Send to Subcontractor'
|
||||||
se.save()
|
se.save()
|
||||||
|
54
erpnext/change_log/v13/v13_5_0.md
Normal file
54
erpnext/change_log/v13/v13_5_0.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Version 13.5.0 Release Notes
|
||||||
|
|
||||||
|
### Features & Enhancements
|
||||||
|
|
||||||
|
- Tax deduction against advance payments ([#25831](https://github.com/frappe/erpnext/pull/25831))
|
||||||
|
- Cost-center wise period closing entry ([#25766](https://github.com/frappe/erpnext/pull/25766))
|
||||||
|
- Create Quality Inspections from account and stock documents ([#25221](https://github.com/frappe/erpnext/pull/25221))
|
||||||
|
- Item Taxes based on net rate ([#25961](https://github.com/frappe/erpnext/pull/25961))
|
||||||
|
- Enable/disable gl entry posting for change given in pos ([#25822](https://github.com/frappe/erpnext/pull/25822))
|
||||||
|
- Add Inactive status to Employee ([#26029](https://github.com/frappe/erpnext/pull/26029))
|
||||||
|
- Added check box to combine items with same BOM ([#25478](https://github.com/frappe/erpnext/pull/25478))
|
||||||
|
- Item Tax Templates for Germany ([#25858](https://github.com/frappe/erpnext/pull/25858))
|
||||||
|
- Refactored leave balance report ([#25771](https://github.com/frappe/erpnext/pull/25771))
|
||||||
|
- Refactored Vehicle Expenses Report ([#25727](https://github.com/frappe/erpnext/pull/25727))
|
||||||
|
- Refactored maintenance schedule and visit document ([#25358](https://github.com/frappe/erpnext/pull/25358))
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
- Cannot add same item with different rates ([#25849](https://github.com/frappe/erpnext/pull/25849))
|
||||||
|
- Show only company addresses for ITC reversal entry ([#25866](https://github.com/frappe/erpnext/pull/25866))
|
||||||
|
- Hiding Rounding Adjustment field ([#25380](https://github.com/frappe/erpnext/pull/25380))
|
||||||
|
- Auto tax calculations in Payment Entry ([#26055](https://github.com/frappe/erpnext/pull/26055))
|
||||||
|
- Not able to select the item code in work order ([#25915](https://github.com/frappe/erpnext/pull/25915))
|
||||||
|
- Cannot reset plaid link for a bank account ([#25869](https://github.com/frappe/erpnext/pull/25869))
|
||||||
|
- Student invalid password reset link ([#25826](https://github.com/frappe/erpnext/pull/25826))
|
||||||
|
- Multiple pos issues ([#25928](https://github.com/frappe/erpnext/pull/25928))
|
||||||
|
- Add Product Bundles to POS ([#25860](https://github.com/frappe/erpnext/pull/25860))
|
||||||
|
- Enable Parallel tests ([#25862](https://github.com/frappe/erpnext/pull/25862))
|
||||||
|
- Service item check on e-Invoicing ([#25986](https://github.com/frappe/erpnext/pull/25986))
|
||||||
|
- Choose correct Salary Structure Assignment when getting data for formula eval ([#25981](https://github.com/frappe/erpnext/pull/25981))
|
||||||
|
- Ignore internal transfer invoices from GST Reports ([#25969](https://github.com/frappe/erpnext/pull/25969))
|
||||||
|
- Taxable value for invoices with additional discount ([#26056](https://github.com/frappe/erpnext/pull/26056))
|
||||||
|
- Validate negative allocated amount in Payment Entry ([#25799](https://github.com/frappe/erpnext/pull/25799))
|
||||||
|
- Allow all System Managers to delete company transactions ([#25834](https://github.com/frappe/erpnext/pull/25834))
|
||||||
|
- Wrong round off gl entry posted in case of purchase invoice ([#25775](https://github.com/frappe/erpnext/pull/25775))
|
||||||
|
- Use dictionary filter instead of list ([#25874](https://github.com/frappe/erpnext/pull/25874))
|
||||||
|
- Ageing error in PSOA ([#25855](https://github.com/frappe/erpnext/pull/25855))
|
||||||
|
- On click of duplicate button system has not copied the difference account ([#25988](https://github.com/frappe/erpnext/pull/25988))
|
||||||
|
- Assign Product Bundle's conversion_factor to Pack… ([#25840](https://github.com/frappe/erpnext/pull/25840))
|
||||||
|
- Rename Loan Management workspace to Loans ([#25856](https://github.com/frappe/erpnext/pull/25856))
|
||||||
|
- Fix stock quantity calculation when negative_stock_allowe… ([#25859](https://github.com/frappe/erpnext/pull/25859))
|
||||||
|
- Update cost center from pos profile ([#25971](https://github.com/frappe/erpnext/pull/25971))
|
||||||
|
- Ensure website theme is applied correctly ([#25863](https://github.com/frappe/erpnext/pull/25863))
|
||||||
|
- Only display GST card in Accounting Workspace if it's in India ([#26000](https://github.com/frappe/erpnext/pull/26000))
|
||||||
|
- Incorrect gstin fetched incase of branch company address ([#25841](https://github.com/frappe/erpnext/pull/25841))
|
||||||
|
- Sort account balances by account name ([#26009](https://github.com/frappe/erpnext/pull/26009))
|
||||||
|
- Custom conversion factor field not mapped from job card to stock entry ([#25956](https://github.com/frappe/erpnext/pull/25956))
|
||||||
|
- Chart of accounts importer always error ([#25882](https://github.com/frappe/erpnext/pull/25882))
|
||||||
|
- Create POS Invoice for Product Bundles ([#25847](https://github.com/frappe/erpnext/pull/25847))
|
||||||
|
- Wrap dates in getdate for leave application ([#25899](https://github.com/frappe/erpnext/pull/25899))
|
||||||
|
- Closing entry shows incorrect expected amount ([#25868](https://github.com/frappe/erpnext/pull/25868))
|
||||||
|
- Add Hold status column in the Issue Summary Report ([#25828](https://github.com/frappe/erpnext/pull/25828))
|
||||||
|
- Rendering of broken image on pos ([#25872](https://github.com/frappe/erpnext/pull/25872))
|
||||||
|
- Timeout error in the repost item valuation ([#25854](https://github.com/frappe/erpnext/pull/25854))
|
@ -116,6 +116,8 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
if self.doctype == 'Purchase Invoice':
|
if self.doctype == 'Purchase Invoice':
|
||||||
self.calculate_paid_amount()
|
self.calculate_paid_amount()
|
||||||
|
# apply tax withholding only if checked and applicable
|
||||||
|
self.set_tax_withholding()
|
||||||
|
|
||||||
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
|
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
|
||||||
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
|
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
|
||||||
@ -225,7 +227,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
def validate_date_with_fiscal_year(self):
|
def validate_date_with_fiscal_year(self):
|
||||||
if self.meta.get_field("fiscal_year"):
|
if self.meta.get_field("fiscal_year"):
|
||||||
date_field = ""
|
date_field = None
|
||||||
if self.meta.get_field("posting_date"):
|
if self.meta.get_field("posting_date"):
|
||||||
date_field = "posting_date"
|
date_field = "posting_date"
|
||||||
elif self.meta.get_field("transaction_date"):
|
elif self.meta.get_field("transaction_date"):
|
||||||
@ -700,6 +702,7 @@ class AccountsController(TransactionBase):
|
|||||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||||
|
|
||||||
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
|
self.update_allocated_advance_taxes_on_cancel()
|
||||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||||
unlink_ref_doc_from_payment_entries(self)
|
unlink_ref_doc_from_payment_entries(self)
|
||||||
|
|
||||||
@ -707,6 +710,87 @@ class AccountsController(TransactionBase):
|
|||||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
|
if frappe.db.get_single_value('Accounts Settings', 'unlink_advance_payment_on_cancelation_of_order'):
|
||||||
unlink_ref_doc_from_payment_entries(self)
|
unlink_ref_doc_from_payment_entries(self)
|
||||||
|
|
||||||
|
def get_tax_map(self):
|
||||||
|
tax_map = {}
|
||||||
|
for tax in self.get('taxes'):
|
||||||
|
tax_map.setdefault(tax.account_head, 0.0)
|
||||||
|
tax_map[tax.account_head] += tax.tax_amount
|
||||||
|
|
||||||
|
return tax_map
|
||||||
|
|
||||||
|
def update_allocated_advance_taxes_on_cancel(self):
|
||||||
|
if self.get('advances'):
|
||||||
|
tax_accounts = [d.account_head for d in self.get('taxes')]
|
||||||
|
allocated_tax_map = frappe._dict(frappe.get_all('GL Entry', fields=['account', 'sum(credit - debit)'],
|
||||||
|
filters={'voucher_no': self.name, 'account': ('in', tax_accounts)},
|
||||||
|
group_by='account', as_list=1))
|
||||||
|
|
||||||
|
tax_map = self.get_tax_map()
|
||||||
|
|
||||||
|
for pe in self.get('advances'):
|
||||||
|
if pe.reference_type == 'Payment Entry':
|
||||||
|
pe = frappe.get_doc('Payment Entry', pe.reference_name)
|
||||||
|
for tax in pe.get('taxes'):
|
||||||
|
allocated_amount = tax_map.get(tax.account_head) - allocated_tax_map.get(tax.account_head)
|
||||||
|
if allocated_amount > tax.tax_amount:
|
||||||
|
allocated_amount = tax.tax_amount
|
||||||
|
|
||||||
|
if allocated_amount:
|
||||||
|
frappe.db.set_value('Advance Taxes and Charges', tax.name, 'allocated_amount',
|
||||||
|
tax.allocated_amount - allocated_amount)
|
||||||
|
tax_map[tax.account_head] -= allocated_amount
|
||||||
|
allocated_tax_map[tax.account_head] -= allocated_amount
|
||||||
|
|
||||||
|
def allocate_advance_taxes(self, gl_entries):
|
||||||
|
tax_map = self.get_tax_map()
|
||||||
|
for pe in self.get("advances"):
|
||||||
|
if pe.reference_type == "Payment Entry" and \
|
||||||
|
frappe.db.get_value('Payment Entry', pe.reference_name, 'advance_tax_account'):
|
||||||
|
pe = frappe.get_doc("Payment Entry", pe.reference_name)
|
||||||
|
for tax in pe.get("taxes"):
|
||||||
|
account_currency = get_account_currency(tax.account_head)
|
||||||
|
|
||||||
|
if self.doctype == "Purchase Invoice":
|
||||||
|
dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
|
||||||
|
rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
|
||||||
|
else:
|
||||||
|
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
|
||||||
|
rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
|
||||||
|
|
||||||
|
party = self.supplier if self.doctype == "Purchase Invoice" else self.customer
|
||||||
|
unallocated_amount = tax.tax_amount - tax.allocated_amount
|
||||||
|
if tax_map.get(tax.account_head):
|
||||||
|
amount = tax_map.get(tax.account_head)
|
||||||
|
if amount < unallocated_amount:
|
||||||
|
unallocated_amount = amount
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": tax.account_head,
|
||||||
|
"against": party,
|
||||||
|
dr_or_cr: unallocated_amount,
|
||||||
|
dr_or_cr + "_in_account_currency": unallocated_amount
|
||||||
|
if account_currency==self.company_currency
|
||||||
|
else unallocated_amount,
|
||||||
|
"cost_center": tax.cost_center
|
||||||
|
}, account_currency, item=tax))
|
||||||
|
|
||||||
|
gl_entries.append(
|
||||||
|
self.get_gl_dict({
|
||||||
|
"account": pe.advance_tax_account,
|
||||||
|
"against": party,
|
||||||
|
rev_dr_cr: unallocated_amount,
|
||||||
|
rev_dr_cr + "_in_account_currency": unallocated_amount
|
||||||
|
if account_currency==self.company_currency
|
||||||
|
else unallocated_amount,
|
||||||
|
"cost_center": tax.cost_center
|
||||||
|
}, account_currency, item=tax))
|
||||||
|
|
||||||
|
frappe.db.set_value("Advance Taxes and Charges", tax.name, "allocated_amount",
|
||||||
|
tax.allocated_amount + unallocated_amount)
|
||||||
|
|
||||||
|
tax_map[tax.account_head] -= unallocated_amount
|
||||||
|
|
||||||
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
||||||
from erpnext.controllers.status_updater import get_allowance_for
|
from erpnext.controllers.status_updater import get_allowance_for
|
||||||
item_allowance = {}
|
item_allowance = {}
|
||||||
@ -1011,7 +1095,6 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
grand_total -= self.get("total_advance")
|
grand_total -= self.get("total_advance")
|
||||||
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
|
base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
|
||||||
|
|
||||||
if total != flt(grand_total, self.precision("grand_total")) or \
|
if total != flt(grand_total, self.precision("grand_total")) or \
|
||||||
base_total != flt(base_grand_total, self.precision("base_grand_total")):
|
base_total != flt(base_grand_total, self.precision("base_grand_total")):
|
||||||
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
|
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
|
||||||
@ -1109,7 +1192,7 @@ def validate_conversion_rate(currency, conversion_rate, conversion_rate_label, c
|
|||||||
|
|
||||||
|
|
||||||
def validate_taxes_and_charges(tax):
|
def validate_taxes_and_charges(tax):
|
||||||
if tax.charge_type in ['Actual', 'On Net Total'] and tax.row_id:
|
if tax.charge_type in ['Actual', 'On Net Total', 'On Paid Amount'] and tax.row_id:
|
||||||
frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"))
|
frappe.throw(_("Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total'"))
|
||||||
elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']:
|
elif tax.charge_type in ['On Previous Row Amount', 'On Previous Row Total']:
|
||||||
if cint(tax.idx) == 1:
|
if cint(tax.idx) == 1:
|
||||||
@ -1126,20 +1209,19 @@ def validate_taxes_and_charges(tax):
|
|||||||
|
|
||||||
def validate_inclusive_tax(tax, doc):
|
def validate_inclusive_tax(tax, doc):
|
||||||
def _on_previous_row_error(row_range):
|
def _on_previous_row_error(row_range):
|
||||||
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx,
|
throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range))
|
||||||
row_range))
|
|
||||||
|
|
||||||
if cint(getattr(tax, "included_in_print_rate", None)):
|
if cint(getattr(tax, "included_in_print_rate", None)):
|
||||||
if tax.charge_type == "Actual":
|
if tax.charge_type == "Actual":
|
||||||
# inclusive tax cannot be of type Actual
|
# inclusive tax cannot be of type Actual
|
||||||
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate").format(tax.idx))
|
throw(_("Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount").format(tax.idx))
|
||||||
elif tax.charge_type == "On Previous Row Amount" and \
|
elif tax.charge_type == "On Previous Row Amount" and \
|
||||||
not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate):
|
not cint(doc.get("taxes")[cint(tax.row_id) - 1].included_in_print_rate):
|
||||||
# referred row should also be inclusive
|
# referred row should also be inclusive
|
||||||
_on_previous_row_error(tax.row_id)
|
_on_previous_row_error(tax.row_id)
|
||||||
elif tax.charge_type == "On Previous Row Total" and \
|
elif tax.charge_type == "On Previous Row Total" and \
|
||||||
not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]):
|
not all([cint(t.included_in_print_rate) for t in doc.get("taxes")[:cint(tax.row_id) - 1]]):
|
||||||
# all rows about the reffered tax should be inclusive
|
# all rows about the referred tax should be inclusive
|
||||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||||
elif tax.get("category") == "Valuation":
|
elif tax.get("category") == "Valuation":
|
||||||
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||||
@ -1241,7 +1323,6 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype,
|
|||||||
|
|
||||||
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
return list(payment_entries_against_order) + list(unallocated_payment_entries)
|
||||||
|
|
||||||
|
|
||||||
def update_invoice_status():
|
def update_invoice_status():
|
||||||
# Daily update the status of the invoices
|
# Daily update the status of the invoices
|
||||||
|
|
||||||
@ -1450,6 +1531,7 @@ def validate_and_delete_children(parent, data):
|
|||||||
for d in deleted_children:
|
for d in deleted_children:
|
||||||
update_bin_on_delete(d, parent.doctype)
|
update_bin_on_delete(d, parent.doctype)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
|
||||||
def check_doc_permissions(doc, perm_type='create'):
|
def check_doc_permissions(doc, perm_type='create'):
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
import erpnext
|
import erpnext
|
||||||
|
import json
|
||||||
from frappe.desk.reportview import get_match_cond, get_filters_cond
|
from frappe.desk.reportview import get_match_cond, get_filters_cond
|
||||||
from frappe.utils import nowdate, getdate
|
from frappe.utils import nowdate, getdate
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
@ -198,6 +199,9 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
||||||
conditions = []
|
conditions = []
|
||||||
|
|
||||||
|
if isinstance(filters, str):
|
||||||
|
filters = json.loads(filters)
|
||||||
|
|
||||||
#Get searchfields from meta and use in Item Link field query
|
#Get searchfields from meta and use in Item Link field query
|
||||||
meta = frappe.get_meta("Item", cached=True)
|
meta = frappe.get_meta("Item", cached=True)
|
||||||
searchfields = meta.get_search_fields()
|
searchfields = meta.get_search_fields()
|
||||||
|
@ -1,17 +1,21 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
import json
|
||||||
import frappe, erpnext
|
|
||||||
from frappe.utils import cint, flt, cstr, get_link_to_form, today, getdate
|
|
||||||
from frappe import _
|
|
||||||
import frappe.defaults
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from erpnext.accounts.utils import get_fiscal_year, check_if_stock_and_account_balance_synced
|
|
||||||
|
import frappe
|
||||||
|
import frappe.defaults
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
|
||||||
|
|
||||||
|
import erpnext
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries, process_gl_map
|
||||||
|
from erpnext.accounts.utils import check_if_stock_and_account_balance_synced, get_fiscal_year
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.accounts_controller import AccountsController
|
||||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
|
||||||
from erpnext.stock import get_warehouse_account_map
|
from erpnext.stock import get_warehouse_account_map
|
||||||
|
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||||
|
|
||||||
|
|
||||||
class QualityInspectionRequiredError(frappe.ValidationError): pass
|
class QualityInspectionRequiredError(frappe.ValidationError): pass
|
||||||
class QualityInspectionRejectedError(frappe.ValidationError): pass
|
class QualityInspectionRejectedError(frappe.ValidationError): pass
|
||||||
@ -189,7 +193,6 @@ class StockController(AccountsController):
|
|||||||
if hasattr(self, "items"):
|
if hasattr(self, "items"):
|
||||||
item_doclist = self.get("items")
|
item_doclist = self.get("items")
|
||||||
elif self.doctype == "Stock Reconciliation":
|
elif self.doctype == "Stock Reconciliation":
|
||||||
import json
|
|
||||||
item_doclist = []
|
item_doclist = []
|
||||||
data = json.loads(self.reconciliation_json)
|
data = json.loads(self.reconciliation_json)
|
||||||
for row in data[data.index(self.head_row)+1:]:
|
for row in data[data.index(self.head_row)+1:]:
|
||||||
@ -319,7 +322,7 @@ class StockController(AccountsController):
|
|||||||
return serialized_items
|
return serialized_items
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self):
|
||||||
from erpnext.stock.utils import validate_warehouse_company, validate_disabled_warehouse
|
from erpnext.stock.utils import validate_disabled_warehouse, validate_warehouse_company
|
||||||
|
|
||||||
warehouses = list(set([d.warehouse for d in
|
warehouses = list(set([d.warehouse for d in
|
||||||
self.get("items") if getattr(d, "warehouse", None)]))
|
self.get("items") if getattr(d, "warehouse", None)]))
|
||||||
@ -498,6 +501,39 @@ class StockController(AccountsController):
|
|||||||
check_if_stock_and_account_balance_synced(self.posting_date,
|
check_if_stock_and_account_balance_synced(self.posting_date,
|
||||||
self.company, self.doctype, self.name)
|
self.company, self.doctype, self.name)
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_quality_inspections(doctype, docname, items):
|
||||||
|
if isinstance(items, str):
|
||||||
|
items = json.loads(items)
|
||||||
|
|
||||||
|
inspections = []
|
||||||
|
for item in items:
|
||||||
|
if flt(item.get("sample_size")) > flt(item.get("qty")):
|
||||||
|
frappe.throw(_("{item_name}'s Sample Size ({sample_size}) cannot be greater than the Accepted Quantity ({accepted_quantity})").format(
|
||||||
|
item_name=item.get("item_name"),
|
||||||
|
sample_size=item.get("sample_size"),
|
||||||
|
accepted_quantity=item.get("qty")
|
||||||
|
))
|
||||||
|
|
||||||
|
quality_inspection = frappe.get_doc({
|
||||||
|
"doctype": "Quality Inspection",
|
||||||
|
"inspection_type": "Incoming",
|
||||||
|
"inspected_by": frappe.session.user,
|
||||||
|
"reference_type": doctype,
|
||||||
|
"reference_name": docname,
|
||||||
|
"item_code": item.get("item_code"),
|
||||||
|
"description": item.get("description"),
|
||||||
|
"sample_size": flt(item.get("sample_size")),
|
||||||
|
"item_serial_no": item.get("serial_no").split("\n")[0] if item.get("serial_no") else None,
|
||||||
|
"batch_no": item.get("batch_no")
|
||||||
|
}).insert()
|
||||||
|
quality_inspection.save()
|
||||||
|
inspections.append(quality_inspection.name)
|
||||||
|
|
||||||
|
return inspections
|
||||||
|
|
||||||
|
|
||||||
def is_reposting_pending():
|
def is_reposting_pending():
|
||||||
return frappe.db.exists("Repost Item Valuation",
|
return frappe.db.exists("Repost Item Valuation",
|
||||||
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
||||||
|
@ -54,6 +54,7 @@ class calculate_taxes_and_totals(object):
|
|||||||
if item.item_code and item.get('item_tax_template'):
|
if item.item_code and item.get('item_tax_template'):
|
||||||
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
||||||
args = {
|
args = {
|
||||||
|
'net_rate': item.net_rate or item.rate,
|
||||||
'tax_category': self.doc.get('tax_category'),
|
'tax_category': self.doc.get('tax_category'),
|
||||||
'posting_date': self.doc.get('posting_date'),
|
'posting_date': self.doc.get('posting_date'),
|
||||||
'bill_date': self.doc.get('bill_date'),
|
'bill_date': self.doc.get('bill_date'),
|
||||||
@ -77,8 +78,10 @@ class calculate_taxes_and_totals(object):
|
|||||||
|
|
||||||
taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
|
taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
|
||||||
|
|
||||||
|
if taxes:
|
||||||
if item.item_tax_template not in taxes:
|
if item.item_tax_template not in taxes:
|
||||||
frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
|
item.item_tax_template = taxes[0]
|
||||||
|
frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
|
||||||
item.idx, frappe.bold(item.item_code)
|
item.idx, frappe.bold(item.item_code)
|
||||||
))
|
))
|
||||||
|
|
||||||
@ -683,7 +686,6 @@ class calculate_taxes_and_totals(object):
|
|||||||
|
|
||||||
self.calculate_paid_amount()
|
self.calculate_paid_amount()
|
||||||
|
|
||||||
|
|
||||||
def get_itemised_tax_breakup_html(doc):
|
def get_itemised_tax_breakup_html(doc):
|
||||||
if not doc.taxes:
|
if not doc.taxes:
|
||||||
return
|
return
|
||||||
|
@ -38,7 +38,7 @@ class Appointment(Document):
|
|||||||
number_of_agents = frappe.db.get_single_value('Appointment Booking Settings', 'number_of_agents')
|
number_of_agents = frappe.db.get_single_value('Appointment Booking Settings', 'number_of_agents')
|
||||||
if not number_of_agents == 0:
|
if not number_of_agents == 0:
|
||||||
if (number_of_appointments_in_same_slot >= number_of_agents):
|
if (number_of_appointments_in_same_slot >= number_of_agents):
|
||||||
frappe.throw('Time slot is not available')
|
frappe.throw(_('Time slot is not available'))
|
||||||
# Link lead
|
# Link lead
|
||||||
if not self.party:
|
if not self.party:
|
||||||
lead = self.find_lead_by_email()
|
lead = self.find_lead_by_email()
|
||||||
@ -75,10 +75,10 @@ class Appointment(Document):
|
|||||||
subject=_('Appointment Confirmation'))
|
subject=_('Appointment Confirmation'))
|
||||||
if frappe.session.user == "Guest":
|
if frappe.session.user == "Guest":
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
'Please check your email to confirm the appointment')
|
_('Please check your email to confirm the appointment'))
|
||||||
else :
|
else :
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
'Appointment was created. But no lead was found. Please check the email to confirm')
|
_('Appointment was created. But no lead was found. Please check the email to confirm'))
|
||||||
|
|
||||||
def on_change(self):
|
def on_change(self):
|
||||||
# Sync Calendar
|
# Sync Calendar
|
||||||
@ -91,7 +91,7 @@ class Appointment(Document):
|
|||||||
|
|
||||||
def set_verified(self, email):
|
def set_verified(self, email):
|
||||||
if not email == self.customer_email:
|
if not email == self.customer_email:
|
||||||
frappe.throw('Email verification failed.')
|
frappe.throw(_('Email verification failed.'))
|
||||||
# Create new lead
|
# Create new lead
|
||||||
self.create_lead_and_link()
|
self.create_lead_and_link()
|
||||||
# Remove unverified status
|
# Remove unverified status
|
||||||
|
@ -280,7 +280,6 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:",
|
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Territory",
|
"label": "Territory",
|
||||||
@ -431,7 +430,7 @@
|
|||||||
"icon": "fa fa-info-sign",
|
"icon": "fa fa-info-sign",
|
||||||
"idx": 195,
|
"idx": 195,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-06 19:42:46.190051",
|
"modified": "2021-06-04 10:11:22.831139",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Opportunity",
|
"name": "Opportunity",
|
||||||
|
@ -75,7 +75,7 @@ class CourseSchedulingTool(Document):
|
|||||||
"""Validates if Course Start Date is greater than Course End Date"""
|
"""Validates if Course Start Date is greater than Course End Date"""
|
||||||
if self.course_start_date > self.course_end_date:
|
if self.course_start_date > self.course_end_date:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
"Course Start Date cannot be greater than Course End Date.")
|
_("Course Start Date cannot be greater than Course End Date."))
|
||||||
|
|
||||||
def delete_course_schedule(self, rescheduled, reschedule_errors):
|
def delete_course_schedule(self, rescheduled, reschedule_errors):
|
||||||
"""Delete all course schedule within the Date range and specified filters"""
|
"""Delete all course schedule within the Date range and specified filters"""
|
||||||
|
@ -9,8 +9,7 @@ from frappe.utils import nowdate
|
|||||||
from frappe.utils.make_random import get_random
|
from frappe.utils.make_random import get_random
|
||||||
from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
|
from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
|
||||||
|
|
||||||
# test_records = frappe.get_test_records('Fees')
|
test_dependencies = ['Company']
|
||||||
|
|
||||||
class TestFees(unittest.TestCase):
|
class TestFees(unittest.TestCase):
|
||||||
|
|
||||||
def test_fees(self):
|
def test_fees(self):
|
||||||
|
@ -74,7 +74,6 @@ class Student(Document):
|
|||||||
student_user.flags.ignore_permissions = True
|
student_user.flags.ignore_permissions = True
|
||||||
student_user.add_roles("Student")
|
student_user.add_roles("Student")
|
||||||
student_user.save()
|
student_user.save()
|
||||||
update_password_link = student_user.reset_password()
|
|
||||||
|
|
||||||
def update_applicant_status(self):
|
def update_applicant_status(self):
|
||||||
"""Updates Student Applicant status to Admitted"""
|
"""Updates Student Applicant status to Admitted"""
|
||||||
|
@ -219,7 +219,6 @@ def get_quiz(quiz_name, course):
|
|||||||
try:
|
try:
|
||||||
quiz = frappe.get_doc("Quiz", quiz_name)
|
quiz = frappe.get_doc("Quiz", quiz_name)
|
||||||
questions = quiz.get_questions()
|
questions = quiz.get_questions()
|
||||||
duration = quiz.duration
|
|
||||||
except:
|
except:
|
||||||
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
|
frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
|
||||||
return None
|
return None
|
||||||
@ -236,7 +235,8 @@ def get_quiz(quiz_name, course):
|
|||||||
return {
|
return {
|
||||||
'questions': questions,
|
'questions': questions,
|
||||||
'activity': None,
|
'activity': None,
|
||||||
'duration':duration
|
'is_time_bound': quiz.is_time_bound,
|
||||||
|
'duration': quiz.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
student = get_current_student()
|
student = get_current_student()
|
||||||
@ -245,6 +245,7 @@ def get_quiz(quiz_name, course):
|
|||||||
return {
|
return {
|
||||||
'questions': questions,
|
'questions': questions,
|
||||||
'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken},
|
'activity': {'is_complete': status, 'score': score, 'result': result, 'time_taken': time_taken},
|
||||||
|
'is_time_bound': quiz.is_time_bound,
|
||||||
'duration': quiz.duration
|
'duration': quiz.duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,5 +99,7 @@ class PlaidConnector():
|
|||||||
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
|
||||||
transactions.extend(response["transactions"])
|
transactions.extend(response["transactions"])
|
||||||
return transactions
|
return transactions
|
||||||
|
except ItemError as e:
|
||||||
|
raise e
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
|
||||||
|
@ -16,6 +16,10 @@ frappe.ui.form.on('Plaid Settings', {
|
|||||||
new erpnext.integrations.plaidLink(frm);
|
new erpnext.integrations.plaidLink(frm);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.add_custom_button(__('Reset Plaid Link'), () => {
|
||||||
|
new erpnext.integrations.plaidLink(frm);
|
||||||
|
});
|
||||||
|
|
||||||
frm.add_custom_button(__("Sync Now"), () => {
|
frm.add_custom_button(__("Sync Now"), () => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.enqueue_synchronization",
|
method: "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.enqueue_synchronization",
|
||||||
|
@ -12,6 +12,7 @@ from frappe.desk.doctype.tag.tag import add_tag
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import add_months, formatdate, getdate, today
|
from frappe.utils import add_months, formatdate, getdate, today
|
||||||
|
|
||||||
|
from plaid.errors import ItemError
|
||||||
|
|
||||||
class PlaidSettings(Document):
|
class PlaidSettings(Document):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -51,7 +52,7 @@ def add_institution(token, response):
|
|||||||
})
|
})
|
||||||
bank.insert()
|
bank.insert()
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.log_error(frappe.get_traceback(), title=_('Plaid Link Error'))
|
||||||
else:
|
else:
|
||||||
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
bank = frappe.get_doc("Bank", response["institution"]["name"])
|
||||||
bank.plaid_access_token = access_token
|
bank.plaid_access_token = access_token
|
||||||
@ -83,7 +84,12 @@ def add_bank_accounts(response, bank, company):
|
|||||||
if not acc_subtype:
|
if not acc_subtype:
|
||||||
add_account_subtype(account["subtype"])
|
add_account_subtype(account["subtype"])
|
||||||
|
|
||||||
if not frappe.db.exists("Bank Account", dict(integration_id=account["id"])):
|
existing_bank_account = frappe.db.exists("Bank Account", {
|
||||||
|
'account_name': account["name"],
|
||||||
|
'bank': bank["bank_name"]
|
||||||
|
})
|
||||||
|
|
||||||
|
if not existing_bank_account:
|
||||||
try:
|
try:
|
||||||
new_account = frappe.get_doc({
|
new_account = frappe.get_doc({
|
||||||
"doctype": "Bank Account",
|
"doctype": "Bank Account",
|
||||||
@ -103,10 +109,27 @@ def add_bank_accounts(response, bank, company):
|
|||||||
except frappe.UniqueValidationError:
|
except frappe.UniqueValidationError:
|
||||||
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
|
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
|
||||||
except Exception:
|
except Exception:
|
||||||
frappe.throw(frappe.get_traceback())
|
frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
|
||||||
|
frappe.throw(_("There was an error creating Bank Account while linking with Plaid."),
|
||||||
|
title=_("Plaid Link Failed"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
result.append(frappe.db.get_value("Bank Account", dict(integration_id=account["id"]), "name"))
|
try:
|
||||||
|
existing_account = frappe.get_doc('Bank Account', existing_bank_account)
|
||||||
|
existing_account.update({
|
||||||
|
"bank": bank["bank_name"],
|
||||||
|
"account_name": account["name"],
|
||||||
|
"account_type": account.get("type", ""),
|
||||||
|
"account_subtype": account.get("subtype", ""),
|
||||||
|
"mask": account.get("mask", ""),
|
||||||
|
"integration_id": account["id"]
|
||||||
|
})
|
||||||
|
existing_account.save()
|
||||||
|
result.append(existing_bank_account)
|
||||||
|
except Exception:
|
||||||
|
frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
|
||||||
|
frappe.throw(_("There was an error updating Bank Account {} while linking with Plaid.").format(
|
||||||
|
existing_bank_account), title=_("Plaid Link Failed"))
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -172,9 +195,16 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
|
|||||||
account_id = None
|
account_id = None
|
||||||
|
|
||||||
plaid = PlaidConnector(access_token)
|
plaid = PlaidConnector(access_token)
|
||||||
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
|
|
||||||
|
|
||||||
return transactions
|
try:
|
||||||
|
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
|
||||||
|
except ItemError as e:
|
||||||
|
if e.code == "ITEM_LOGIN_REQUIRED":
|
||||||
|
msg = _("There was an error syncing transactions.") + " "
|
||||||
|
msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
|
||||||
|
frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
|
||||||
|
|
||||||
|
return transactions or []
|
||||||
|
|
||||||
|
|
||||||
def new_bank_transaction(transaction):
|
def new_bank_transaction(transaction):
|
||||||
|
@ -332,7 +332,9 @@ scheduler_events = {
|
|||||||
"erpnext.projects.doctype.project.project.collect_project_status",
|
"erpnext.projects.doctype.project.project.collect_project_status",
|
||||||
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
|
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
|
||||||
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
|
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
|
||||||
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
|
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders"
|
||||||
|
],
|
||||||
|
"hourly_long": [
|
||||||
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
|
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
|
||||||
],
|
],
|
||||||
"daily": [
|
"daily": [
|
||||||
|
@ -207,7 +207,7 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Active\nLeft",
|
"options": "Active\nInactive\nLeft",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@ -813,7 +813,7 @@
|
|||||||
"idx": 24,
|
"idx": 24,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-02 16:54:33.477439",
|
"modified": "2021-06-12 11:31:37.730760",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee",
|
"name": "Employee",
|
||||||
|
@ -37,7 +37,7 @@ class Employee(NestedSet):
|
|||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
from erpnext.controllers.status_updater import validate_status
|
from erpnext.controllers.status_updater import validate_status
|
||||||
validate_status(self.status, ["Active", "Temporary Leave", "Left"])
|
validate_status(self.status, ["Active", "Inactive", "Left"])
|
||||||
|
|
||||||
self.employee = self.name
|
self.employee = self.name
|
||||||
self.set_employee_name()
|
self.set_employee_name()
|
||||||
@ -478,7 +478,7 @@ def get_employee_emails(employee_list):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False):
|
def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False):
|
||||||
|
|
||||||
filters = [['status', '!=', 'Left']]
|
filters = [['status', '=', 'Active']]
|
||||||
if company and company != 'All Companies':
|
if company and company != 'All Companies':
|
||||||
filters.append(['company', '=', company])
|
filters.append(['company', '=', company])
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ frappe.listview_settings['Employee'] = {
|
|||||||
filters: [["status","=", "Active"]],
|
filters: [["status","=", "Active"]],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
|
var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
|
||||||
indicator[1] = {"Active": "green", "Temporary Leave": "red", "Left": "gray"}[doc.status];
|
indicator[1] = {"Active": "green", "Inactive": "red", "Left": "gray"}[doc.status];
|
||||||
return indicator;
|
return indicator;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11,8 +11,8 @@ from erpnext.hr.utils import update_employee
|
|||||||
|
|
||||||
class EmployeePromotion(Document):
|
class EmployeePromotion(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if frappe.get_value("Employee", self.employee, "status") == "Left":
|
if frappe.get_value("Employee", self.employee, "status") != "Active":
|
||||||
frappe.throw(_("Cannot promote Employee with status Left"))
|
frappe.throw(_("Cannot promote Employee with status Left or Inactive"))
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
if getdate(self.promotion_date) > getdate():
|
if getdate(self.promotion_date) > getdate():
|
||||||
|
@ -11,8 +11,8 @@ from erpnext.hr.utils import update_employee
|
|||||||
|
|
||||||
class EmployeeTransfer(Document):
|
class EmployeeTransfer(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if frappe.get_value("Employee", self.employee, "status") == "Left":
|
if frappe.get_value("Employee", self.employee, "status") != "Active":
|
||||||
frappe.throw(_("Cannot transfer Employee with status Left"))
|
frappe.throw(_("Cannot transfer Employee with status Left or Inactive"))
|
||||||
|
|
||||||
def before_submit(self):
|
def before_submit(self):
|
||||||
if getdate(self.transfer_date) > getdate():
|
if getdate(self.transfer_date) > getdate():
|
||||||
|
@ -4,8 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \
|
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate
|
||||||
comma_or, get_fullname, add_days, nowdate, get_datetime_str
|
|
||||||
from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver
|
from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver
|
||||||
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
@ -85,7 +84,7 @@ class LeaveApplication(Document):
|
|||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"):
|
if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"):
|
||||||
if self.from_date and self.from_date < frappe.utils.today():
|
if self.from_date and getdate(self.from_date) < getdate():
|
||||||
allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application")
|
allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application")
|
||||||
if allowed_role not in frappe.get_roles():
|
if allowed_role not in frappe.get_roles():
|
||||||
frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role))
|
frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role))
|
||||||
@ -248,9 +247,9 @@ class LeaveApplication(Document):
|
|||||||
self.throw_overlap_error(d)
|
self.throw_overlap_error(d)
|
||||||
|
|
||||||
def throw_overlap_error(self, d):
|
def throw_overlap_error(self, d):
|
||||||
msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee,
|
form_link = get_link_to_form("Leave Application", d.name)
|
||||||
d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \
|
msg = _("Employee {0} has already applied for {1} between {2} and {3} : {4}").format(self.employee,
|
||||||
+ """ <b><a href="/app/Form/Leave Application/{0}">{0}</a></b>""".format(d["name"])
|
d['leave_type'], formatdate(d['from_date']), formatdate(d['to_date']), form_link)
|
||||||
frappe.throw(msg, OverlapError)
|
frappe.throw(msg, OverlapError)
|
||||||
|
|
||||||
def get_total_leaves_on_half_day(self):
|
def get_total_leaves_on_half_day(self):
|
||||||
@ -356,7 +355,7 @@ class LeaveApplication(Document):
|
|||||||
|
|
||||||
sender = dict()
|
sender = dict()
|
||||||
sender['email'] = frappe.get_doc('User', frappe.session.user).email
|
sender['email'] = frappe.get_doc('User', frappe.session.user).email
|
||||||
sender['full_name'] = frappe.utils.get_fullname(sender['email'])
|
sender['full_name'] = get_fullname(sender['email'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
frappe.sendmail(
|
frappe.sendmail(
|
||||||
|
@ -5,11 +5,18 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import erpnext
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
|
from erpnext.hr.doctype.upload_attendance.upload_attendance import get_data
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
|
test_dependencies = ['Holiday List']
|
||||||
|
|
||||||
class TestUploadAttendance(unittest.TestCase):
|
class TestUploadAttendance(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", '_Test Holiday List')
|
||||||
|
|
||||||
def test_date_range(self):
|
def test_date_range(self):
|
||||||
employee = make_employee("test_employee@company.com")
|
employee = make_employee("test_employee@company.com")
|
||||||
employee_doc = frappe.get_doc("Employee", employee)
|
employee_doc = frappe.get_doc("Employee", employee)
|
||||||
|
@ -24,17 +24,7 @@ class TestVehicleLog(unittest.TestCase):
|
|||||||
frappe.delete_doc("Employee", self.employee_id, force=1)
|
frappe.delete_doc("Employee", self.employee_id, force=1)
|
||||||
|
|
||||||
def test_make_vehicle_log_and_syncing_of_odometer_value(self):
|
def test_make_vehicle_log_and_syncing_of_odometer_value(self):
|
||||||
vehicle_log = frappe.get_doc({
|
vehicle_log = make_vehicle_log(self.license_plate, self.employee_id)
|
||||||
"doctype": "Vehicle Log",
|
|
||||||
"license_plate": cstr(self.license_plate),
|
|
||||||
"employee": self.employee_id,
|
|
||||||
"date":frappe.utils.nowdate(),
|
|
||||||
"odometer":5010,
|
|
||||||
"fuel_qty":frappe.utils.flt(50),
|
|
||||||
"price": frappe.utils.flt(500)
|
|
||||||
})
|
|
||||||
vehicle_log.save()
|
|
||||||
vehicle_log.submit()
|
|
||||||
|
|
||||||
#checking value of vehicle odometer value on submit.
|
#checking value of vehicle odometer value on submit.
|
||||||
vehicle = frappe.get_doc("Vehicle", self.license_plate)
|
vehicle = frappe.get_doc("Vehicle", self.license_plate)
|
||||||
@ -53,17 +43,7 @@ class TestVehicleLog(unittest.TestCase):
|
|||||||
vehicle_log.delete()
|
vehicle_log.delete()
|
||||||
|
|
||||||
def test_vehicle_log_fuel_expense(self):
|
def test_vehicle_log_fuel_expense(self):
|
||||||
vehicle_log = frappe.get_doc({
|
vehicle_log = make_vehicle_log(self.license_plate, self.employee_id)
|
||||||
"doctype": "Vehicle Log",
|
|
||||||
"license_plate": cstr(self.license_plate),
|
|
||||||
"employee": self.employee_id,
|
|
||||||
"date": frappe.utils.nowdate(),
|
|
||||||
"odometer":5010,
|
|
||||||
"fuel_qty":frappe.utils.flt(50),
|
|
||||||
"price": frappe.utils.flt(500)
|
|
||||||
})
|
|
||||||
vehicle_log.save()
|
|
||||||
vehicle_log.submit()
|
|
||||||
|
|
||||||
expense_claim = make_expense_claim(vehicle_log.name)
|
expense_claim = make_expense_claim(vehicle_log.name)
|
||||||
fuel_expense = expense_claim.expenses[0].amount
|
fuel_expense = expense_claim.expenses[0].amount
|
||||||
@ -73,6 +53,18 @@ class TestVehicleLog(unittest.TestCase):
|
|||||||
frappe.delete_doc("Expense Claim", expense_claim.name)
|
frappe.delete_doc("Expense Claim", expense_claim.name)
|
||||||
frappe.delete_doc("Vehicle Log", vehicle_log.name)
|
frappe.delete_doc("Vehicle Log", vehicle_log.name)
|
||||||
|
|
||||||
|
def test_vehicle_log_with_service_expenses(self):
|
||||||
|
vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True)
|
||||||
|
|
||||||
|
expense_claim = make_expense_claim(vehicle_log.name)
|
||||||
|
expenses = expense_claim.expenses[0].amount
|
||||||
|
self.assertEqual(expenses, 27000)
|
||||||
|
|
||||||
|
vehicle_log.cancel()
|
||||||
|
frappe.delete_doc("Expense Claim", expense_claim.name)
|
||||||
|
frappe.delete_doc("Vehicle Log", vehicle_log.name)
|
||||||
|
|
||||||
|
|
||||||
def get_vehicle(employee_id):
|
def get_vehicle(employee_id):
|
||||||
license_plate=random_string(10).upper()
|
license_plate=random_string(10).upper()
|
||||||
vehicle = frappe.get_doc({
|
vehicle = frappe.get_doc({
|
||||||
@ -82,14 +74,45 @@ def get_vehicle(employee_id):
|
|||||||
"model": "PCM",
|
"model": "PCM",
|
||||||
"employee": employee_id,
|
"employee": employee_id,
|
||||||
"last_odometer": 5000,
|
"last_odometer": 5000,
|
||||||
"acquisition_date":frappe.utils.nowdate(),
|
"acquisition_date": nowdate(),
|
||||||
"location": "Mumbai",
|
"location": "Mumbai",
|
||||||
"chassis_no": "1234ABCD",
|
"chassis_no": "1234ABCD",
|
||||||
"uom": "Litre",
|
"uom": "Litre",
|
||||||
"vehicle_value":frappe.utils.flt(500000)
|
"vehicle_value": flt(500000)
|
||||||
})
|
})
|
||||||
try:
|
try:
|
||||||
vehicle.insert()
|
vehicle.insert()
|
||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
return license_plate
|
return license_plate
|
||||||
|
|
||||||
|
|
||||||
|
def make_vehicle_log(license_plate, employee_id, with_services=False):
|
||||||
|
vehicle_log = frappe.get_doc({
|
||||||
|
"doctype": "Vehicle Log",
|
||||||
|
"license_plate": cstr(license_plate),
|
||||||
|
"employee": employee_id,
|
||||||
|
"date": nowdate(),
|
||||||
|
"odometer": 5010,
|
||||||
|
"fuel_qty": flt(50),
|
||||||
|
"price": flt(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
if with_services:
|
||||||
|
vehicle_log.append("service_detail", {
|
||||||
|
"service_item": "Oil Change",
|
||||||
|
"type": "Inspection",
|
||||||
|
"frequency": "Mileage",
|
||||||
|
"expense_amount": flt(500)
|
||||||
|
})
|
||||||
|
vehicle_log.append("service_detail", {
|
||||||
|
"service_item": "Wheels",
|
||||||
|
"type": "Change",
|
||||||
|
"frequency": "Half Yearly",
|
||||||
|
"expense_amount": flt(1500)
|
||||||
|
})
|
||||||
|
|
||||||
|
vehicle_log.save()
|
||||||
|
vehicle_log.submit()
|
||||||
|
|
||||||
|
return vehicle_log
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"creation": "2016-09-03 14:14:51.788550",
|
"creation": "2016-09-03 14:14:51.788550",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -10,7 +11,6 @@
|
|||||||
"naming_series",
|
"naming_series",
|
||||||
"license_plate",
|
"license_plate",
|
||||||
"employee",
|
"employee",
|
||||||
"column_break_4",
|
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"model",
|
"model",
|
||||||
"make",
|
"make",
|
||||||
@ -65,10 +65,6 @@
|
|||||||
"options": "Employee",
|
"options": "Employee",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "column_break_4",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_7",
|
"fieldname": "column_break_7",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
@ -142,7 +138,6 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "service_detail",
|
"fieldname": "service_detail",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Service Detail",
|
|
||||||
"options": "Vehicle Service"
|
"options": "Vehicle Service"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -158,7 +153,7 @@
|
|||||||
"fetch_from": "license_plate.last_odometer",
|
"fetch_from": "license_plate.last_odometer",
|
||||||
"fieldname": "last_odometer",
|
"fieldname": "last_odometer",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"label": "last Odometer Value ",
|
"label": "Last Odometer Value ",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@ -168,7 +163,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2020-03-18 16:45:45.060761",
|
"links": [],
|
||||||
|
"modified": "2021-05-17 00:10:21.188352",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Vehicle Log",
|
"name": "Vehicle Log",
|
||||||
|
@ -37,5 +37,22 @@ frappe.query_reports["Employee Leave Balance"] = {
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Employee",
|
"options": "Employee",
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
|
||||||
|
onload: () => {
|
||||||
|
frappe.call({
|
||||||
|
type: "GET",
|
||||||
|
method: "erpnext.hr.utils.get_leave_period",
|
||||||
|
args: {
|
||||||
|
"from_date": frappe.defaults.get_default("year_start_date"),
|
||||||
|
"to_date": frappe.defaults.get_default("year_end_date"),
|
||||||
|
"company": frappe.defaults.get_user_default("Company")
|
||||||
|
},
|
||||||
|
freeze: true,
|
||||||
|
callback: (data) => {
|
||||||
|
frappe.query_report.set_filter_value("from_date", data.message[0].from_date);
|
||||||
|
frappe.query_report.set_filter_value("to_date", data.message[0].to_date);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,16 @@ import frappe
|
|||||||
from frappe.utils import flt, add_days
|
from frappe.utils import flt, add_days
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
|
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period, get_leave_balance_on
|
||||||
|
from itertools import groupby
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
if filters.to_date <= filters.from_date:
|
if filters.to_date <= filters.from_date:
|
||||||
frappe.throw(_('"From date" can not be greater than or equal to "To date"'))
|
frappe.throw(_('"From Date" can not be greater than or equal to "To Date"'))
|
||||||
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
data = get_data(filters)
|
data = get_data(filters)
|
||||||
|
charts = get_chart_data(data)
|
||||||
return columns, data
|
return columns, data, None, charts
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
columns = [{
|
columns = [{
|
||||||
@ -31,9 +32,10 @@ def get_columns():
|
|||||||
'options': 'Employee'
|
'options': 'Employee'
|
||||||
}, {
|
}, {
|
||||||
'label': _('Employee Name'),
|
'label': _('Employee Name'),
|
||||||
'fieldtype': 'Data',
|
'fieldtype': 'Dynamic Link',
|
||||||
'fieldname': 'employee_name',
|
'fieldname': 'employee_name',
|
||||||
'width': 100,
|
'width': 100,
|
||||||
|
'options': 'employee'
|
||||||
}, {
|
}, {
|
||||||
'label': _('Opening Balance'),
|
'label': _('Opening Balance'),
|
||||||
'fieldtype': 'float',
|
'fieldtype': 'float',
|
||||||
@ -64,8 +66,7 @@ def get_columns():
|
|||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
leave_types = frappe.db.sql_list("SELECT `name` FROM `tabLeave Type` ORDER BY `name` ASC")
|
leave_types = frappe.db.get_list('Leave Type', pluck='name', order_by='name')
|
||||||
|
|
||||||
conditions = get_conditions(filters)
|
conditions = get_conditions(filters)
|
||||||
|
|
||||||
user = frappe.session.user
|
user = frappe.session.user
|
||||||
@ -113,12 +114,8 @@ def get_data(filters):
|
|||||||
|
|
||||||
# not be shown on the basis of days left it create in user mind for carry_forward leave
|
# not be shown on the basis of days left it create in user mind for carry_forward leave
|
||||||
row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken))
|
row.closing_balance = (new_allocation + opening - (row.leaves_expired + leaves_taken))
|
||||||
|
|
||||||
|
|
||||||
row.indent = 1
|
row.indent = 1
|
||||||
data.append(row)
|
data.append(row)
|
||||||
new_leaves_allocated = 0
|
|
||||||
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@ -129,27 +126,37 @@ def get_conditions(filters):
|
|||||||
if filters.get('employee'):
|
if filters.get('employee'):
|
||||||
conditions['name'] = filters.get('employee')
|
conditions['name'] = filters.get('employee')
|
||||||
|
|
||||||
if filters.get('employee'):
|
|
||||||
conditions['name'] = filters.get('employee')
|
|
||||||
|
|
||||||
if filters.get('company'):
|
if filters.get('company'):
|
||||||
conditions['company'] = filters.get('company')
|
conditions['company'] = filters.get('company')
|
||||||
|
|
||||||
|
if filters.get('department'):
|
||||||
|
conditions['department'] = filters.get('department')
|
||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
def get_department_leave_approver_map(department=None):
|
def get_department_leave_approver_map(department=None):
|
||||||
conditions=''
|
|
||||||
if department:
|
|
||||||
conditions="and (department_name = '%(department)s' or parent_department = '%(department)s')"%{'department': department}
|
|
||||||
|
|
||||||
# get current department and all its child
|
# get current department and all its child
|
||||||
department_list = frappe.db.sql_list(""" SELECT name FROM `tabDepartment` WHERE disabled=0 {0}""".format(conditions)) #nosec
|
department_list = frappe.get_list('Department',
|
||||||
|
filters={
|
||||||
|
'disabled': 0
|
||||||
|
},
|
||||||
|
or_filters={
|
||||||
|
'name': department,
|
||||||
|
'parent_department': department
|
||||||
|
},
|
||||||
|
fields=['name'],
|
||||||
|
pluck='name'
|
||||||
|
)
|
||||||
# retrieve approvers list from current department and from its subsequent child departments
|
# retrieve approvers list from current department and from its subsequent child departments
|
||||||
approver_list = frappe.get_all('Department Approver', filters={
|
approver_list = frappe.get_all('Department Approver',
|
||||||
|
filters={
|
||||||
'parentfield': 'leave_approvers',
|
'parentfield': 'leave_approvers',
|
||||||
'parent': ('in', department_list)
|
'parent': ('in', department_list)
|
||||||
}, fields=['parent', 'approver'], as_list=1)
|
},
|
||||||
|
fields=['parent', 'approver'],
|
||||||
|
as_list=1
|
||||||
|
)
|
||||||
|
|
||||||
approvers = {}
|
approvers = {}
|
||||||
|
|
||||||
@ -190,3 +197,40 @@ def get_allocated_and_expired_leaves(from_date, to_date, employee, leave_type):
|
|||||||
new_allocation += record.leaves
|
new_allocation += record.leaves
|
||||||
|
|
||||||
return new_allocation, expired_leaves
|
return new_allocation, expired_leaves
|
||||||
|
|
||||||
|
def get_chart_data(data):
|
||||||
|
labels = []
|
||||||
|
datasets = []
|
||||||
|
employee_data = data
|
||||||
|
|
||||||
|
if data and data[0].get('employee_name'):
|
||||||
|
get_dataset_for_chart(employee_data, datasets, labels)
|
||||||
|
|
||||||
|
chart = {
|
||||||
|
'data': {
|
||||||
|
'labels': labels,
|
||||||
|
'datasets': datasets
|
||||||
|
},
|
||||||
|
'type': 'bar',
|
||||||
|
'colors': ['#456789', '#EE8888', '#7E77BF']
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart
|
||||||
|
|
||||||
|
def get_dataset_for_chart(employee_data, datasets, labels):
|
||||||
|
leaves = []
|
||||||
|
employee_data = sorted(employee_data, key=lambda k: k['employee_name'])
|
||||||
|
|
||||||
|
for key, group in groupby(employee_data, lambda x: x['employee_name']):
|
||||||
|
for grp in group:
|
||||||
|
if grp.closing_balance:
|
||||||
|
leaves.append(frappe._dict({
|
||||||
|
'leave_type': grp.leave_type,
|
||||||
|
'closing_balance': grp.closing_balance
|
||||||
|
}))
|
||||||
|
|
||||||
|
if leaves:
|
||||||
|
labels.append(key)
|
||||||
|
|
||||||
|
for leave in leaves:
|
||||||
|
datasets.append({'name': leave.leave_type, 'values': [leave.closing_balance]})
|
||||||
|
73
erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
Normal file
73
erpnext/hr/report/vehicle_expenses/test_vehicle_expenses.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import unittest
|
||||||
|
import frappe
|
||||||
|
from frappe.utils import getdate
|
||||||
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
from erpnext.hr.doctype.vehicle_log.vehicle_log import make_expense_claim
|
||||||
|
from erpnext.hr.doctype.vehicle_log.test_vehicle_log import get_vehicle, make_vehicle_log
|
||||||
|
from erpnext.hr.report.vehicle_expenses.vehicle_expenses import execute
|
||||||
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
|
class TestVehicleExpenses(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(self):
|
||||||
|
frappe.db.sql('delete from `tabVehicle Log`')
|
||||||
|
|
||||||
|
employee_id = frappe.db.sql('''select name from `tabEmployee` where name="testdriver@example.com"''')
|
||||||
|
self.employee_id = employee_id[0][0] if employee_id else None
|
||||||
|
if not self.employee_id:
|
||||||
|
self.employee_id = make_employee('testdriver@example.com', company='_Test Company')
|
||||||
|
|
||||||
|
self.license_plate = get_vehicle(self.employee_id)
|
||||||
|
|
||||||
|
def test_vehicle_expenses_based_on_fiscal_year(self):
|
||||||
|
vehicle_log = make_vehicle_log(self.license_plate, self.employee_id, with_services=True)
|
||||||
|
expense_claim = make_expense_claim(vehicle_log.name)
|
||||||
|
|
||||||
|
# Based on Fiscal Year
|
||||||
|
filters = {
|
||||||
|
'filter_based_on': 'Fiscal Year',
|
||||||
|
'fiscal_year': get_fiscal_year(getdate())[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
report = execute(filters)
|
||||||
|
|
||||||
|
expected_data = [{
|
||||||
|
'vehicle': self.license_plate,
|
||||||
|
'make': 'Maruti',
|
||||||
|
'model': 'PCM',
|
||||||
|
'location': 'Mumbai',
|
||||||
|
'log_name': vehicle_log.name,
|
||||||
|
'odometer': 5010,
|
||||||
|
'date': getdate(),
|
||||||
|
'fuel_qty': 50.0,
|
||||||
|
'fuel_price': 500.0,
|
||||||
|
'fuel_expense': 25000.0,
|
||||||
|
'service_expense': 2000.0,
|
||||||
|
'employee': self.employee_id
|
||||||
|
}]
|
||||||
|
|
||||||
|
self.assertEqual(report[1], expected_data)
|
||||||
|
|
||||||
|
# Based on Date Range
|
||||||
|
fiscal_year = get_fiscal_year(getdate(), as_dict=True)
|
||||||
|
filters = {
|
||||||
|
'filter_based_on': 'Date Range',
|
||||||
|
'from_date': fiscal_year.year_start_date,
|
||||||
|
'to_date': fiscal_year.year_end_date
|
||||||
|
}
|
||||||
|
|
||||||
|
report = execute(filters)
|
||||||
|
self.assertEqual(report[1], expected_data)
|
||||||
|
|
||||||
|
# clean up
|
||||||
|
vehicle_log.cancel()
|
||||||
|
frappe.delete_doc('Expense Claim', expense_claim.name)
|
||||||
|
frappe.delete_doc('Vehicle Log', vehicle_log.name)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
frappe.delete_doc('Vehicle', self.license_plate, force=1)
|
||||||
|
frappe.delete_doc('Employee', self.employee_id, force=1)
|
@ -1,31 +1,52 @@
|
|||||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
|
||||||
frappe.query_reports["Vehicle Expenses"] = {
|
frappe.query_reports["Vehicle Expenses"] = {
|
||||||
"filters": [
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname": "filter_based_on",
|
||||||
|
"label": __("Filter Based On"),
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": ["Fiscal Year", "Date Range"],
|
||||||
|
"default": ["Fiscal Year"],
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "fiscal_year",
|
"fieldname": "fiscal_year",
|
||||||
"label": __("Fiscal Year"),
|
"label": __("Fiscal Year"),
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"options": "Fiscal Year",
|
"options": "Fiscal Year",
|
||||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||||
|
"depends_on": "eval: doc.filter_based_on == 'Fiscal Year'",
|
||||||
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "from_date",
|
||||||
|
"label": __("From Date"),
|
||||||
|
"fieldtype": "Date",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"on_change": function(query_report) {
|
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||||
var fiscal_year = query_report.get_values().fiscal_year;
|
"default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12)
|
||||||
if (!fiscal_year) {
|
},
|
||||||
return;
|
{
|
||||||
}
|
"fieldname": "to_date",
|
||||||
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
|
"label": __("To Date"),
|
||||||
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
|
"fieldtype": "Date",
|
||||||
|
"reqd": 1,
|
||||||
frappe.query_report.set_filter({
|
"depends_on": "eval: doc.filter_based_on == 'Date Range'",
|
||||||
from_date: fy.year_start_date,
|
"default": frappe.datetime.nowdate()
|
||||||
to_date: fy.year_end_date
|
},
|
||||||
});
|
{
|
||||||
});
|
"fieldname": "vehicle",
|
||||||
}
|
"label": __("Vehicle"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Vehicle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "employee",
|
||||||
|
"label": __("Employee"),
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"options": "Employee"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
});
|
|
||||||
|
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
{
|
{
|
||||||
"add_total_row": 0,
|
"add_total_row": 1,
|
||||||
"apply_user_permissions": 1,
|
"columns": [],
|
||||||
"creation": "2016-09-09 03:33:40.605734",
|
"creation": "2016-09-09 03:33:40.605734",
|
||||||
|
"disable_prepared_report": 0,
|
||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
|
"filters": [],
|
||||||
"idx": 2,
|
"idx": 2,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2017-02-24 19:59:18.641284",
|
"modified": "2021-05-16 22:48:22.767535",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Vehicle Expenses",
|
"name": "Vehicle Expenses",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
"prepared_report": 0,
|
||||||
"ref_doctype": "Vehicle",
|
"ref_doctype": "Vehicle",
|
||||||
"report_name": "Vehicle Expenses",
|
"report_name": "Vehicle Expenses",
|
||||||
"report_type": "Script Report",
|
"report_type": "Script Report",
|
||||||
|
@ -5,86 +5,209 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import erpnext
|
import erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import flt,cstr
|
from frappe.utils import flt
|
||||||
from erpnext.accounts.report.financial_statements import get_period_list
|
from erpnext.accounts.report.financial_statements import get_period_list
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
columns, data, chart = [], [], []
|
filters = frappe._dict(filters or {})
|
||||||
if filters.get('fiscal_year'):
|
|
||||||
company = erpnext.get_default_company()
|
|
||||||
period_list = get_period_list(filters.get('fiscal_year'), filters.get('fiscal_year'),
|
|
||||||
'', '', 'Fiscal Year', 'Monthly', company=company)
|
|
||||||
columns = get_columns()
|
columns = get_columns()
|
||||||
data=get_log_data(filters)
|
data = get_vehicle_log_data(filters)
|
||||||
chart=get_chart_data(data,period_list)
|
chart = get_chart_data(data, filters)
|
||||||
|
|
||||||
return columns, data, None, chart
|
return columns, data, None, chart
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
columns = [_("License") + ":Link/Vehicle:100", _('Create') + ":data:50",
|
return [
|
||||||
_("Model") + ":data:50", _("Location") + ":data:100",
|
{
|
||||||
_("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80",
|
'fieldname': 'vehicle',
|
||||||
_("Date") + ":Date:100", _("Fuel Qty") + ":Float:80",
|
'fieldtype': 'Link',
|
||||||
_("Fuel Price") + ":Float:100",_("Fuel Expense") + ":Float:100",
|
'label': _('Vehicle'),
|
||||||
_("Service Expense") + ":Float:100"
|
'options': 'Vehicle',
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'make',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'label': _('Make'),
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'model',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'label': _('Model'),
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'location',
|
||||||
|
'fieldtype': 'Data',
|
||||||
|
'label': _('Location'),
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'log_name',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'label': _('Vehicle Log'),
|
||||||
|
'options': 'Vehicle Log',
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'odometer',
|
||||||
|
'fieldtype': 'Int',
|
||||||
|
'label': _('Odometer Value'),
|
||||||
|
'width': 120
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'date',
|
||||||
|
'fieldtype': 'Date',
|
||||||
|
'label': _('Date'),
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'fuel_qty',
|
||||||
|
'fieldtype': 'Float',
|
||||||
|
'label': _('Fuel Qty'),
|
||||||
|
'width': 80
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'fuel_price',
|
||||||
|
'fieldtype': 'Float',
|
||||||
|
'label': _('Fuel Price'),
|
||||||
|
'width': 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'fuel_expense',
|
||||||
|
'fieldtype': 'Currency',
|
||||||
|
'label': _('Fuel Expense'),
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'service_expense',
|
||||||
|
'fieldtype': 'Currency',
|
||||||
|
'label': _('Service Expense'),
|
||||||
|
'width': 150
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'fieldname': 'employee',
|
||||||
|
'fieldtype': 'Link',
|
||||||
|
'label': _('Employee'),
|
||||||
|
'options': 'Employee',
|
||||||
|
'width': 150
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
def get_log_data(filters):
|
|
||||||
fy = frappe.db.get_value('Fiscal Year', filters.get('fiscal_year'), ['year_start_date', 'year_end_date'], as_dict=True)
|
def get_vehicle_log_data(filters):
|
||||||
data = frappe.db.sql("""select
|
start_date, end_date = get_period_dates(filters)
|
||||||
vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model",
|
conditions, values = get_conditions(filters)
|
||||||
vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer",
|
|
||||||
log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price",
|
data = frappe.db.sql("""
|
||||||
log.fuel_qty * log.price as "Fuel Expense"
|
SELECT
|
||||||
from
|
vhcl.license_plate as vehicle, vhcl.make, vhcl.model,
|
||||||
|
vhcl.location, log.name as log_name, log.odometer,
|
||||||
|
log.date, log.employee, log.fuel_qty,
|
||||||
|
log.price as fuel_price,
|
||||||
|
log.fuel_qty * log.price as fuel_expense
|
||||||
|
FROM
|
||||||
`tabVehicle` vhcl,`tabVehicle Log` log
|
`tabVehicle` vhcl,`tabVehicle Log` log
|
||||||
where
|
WHERE
|
||||||
vhcl.license_plate = log.license_plate and log.docstatus = 1 and date between %s and %s
|
vhcl.license_plate = log.license_plate
|
||||||
order by date""" ,(fy.year_start_date, fy.year_end_date), as_dict=1)
|
and log.docstatus = 1
|
||||||
dl=list(data)
|
and date between %(start_date)s and %(end_date)s
|
||||||
for row in dl:
|
{0}
|
||||||
row["Service Expense"]= get_service_expense(row["Log"])
|
ORDER BY date""".format(conditions), values, as_dict=1)
|
||||||
return dl
|
|
||||||
|
for row in data:
|
||||||
|
row['service_expense'] = get_service_expense(row.log_name)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def get_conditions(filters):
|
||||||
|
conditions = ''
|
||||||
|
|
||||||
|
start_date, end_date = get_period_dates(filters)
|
||||||
|
values = {
|
||||||
|
'start_date': start_date,
|
||||||
|
'end_date': end_date
|
||||||
|
}
|
||||||
|
|
||||||
|
if filters.employee:
|
||||||
|
conditions += ' and log.employee = %(employee)s'
|
||||||
|
values['employee'] = filters.employee
|
||||||
|
|
||||||
|
if filters.vehicle:
|
||||||
|
conditions += ' and vhcl.license_plate = %(vehicle)s'
|
||||||
|
values['vehicle'] = filters.vehicle
|
||||||
|
|
||||||
|
return conditions, values
|
||||||
|
|
||||||
|
|
||||||
|
def get_period_dates(filters):
|
||||||
|
if filters.filter_based_on == 'Fiscal Year' and filters.fiscal_year:
|
||||||
|
fy = frappe.db.get_value('Fiscal Year', filters.fiscal_year,
|
||||||
|
['year_start_date', 'year_end_date'], as_dict=True)
|
||||||
|
return fy.year_start_date, fy.year_end_date
|
||||||
|
else:
|
||||||
|
return filters.from_date, filters.to_date
|
||||||
|
|
||||||
|
|
||||||
def get_service_expense(logname):
|
def get_service_expense(logname):
|
||||||
expense_amount = frappe.db.sql("""select sum(expense_amount)
|
expense_amount = frappe.db.sql("""
|
||||||
from `tabVehicle Log` log,`tabVehicle Service` ser
|
SELECT sum(expense_amount)
|
||||||
where ser.parent=log.name and log.name=%s""",logname)
|
FROM
|
||||||
return flt(expense_amount[0][0]) if expense_amount else 0
|
`tabVehicle Log` log, `tabVehicle Service` service
|
||||||
|
WHERE
|
||||||
|
service.parent=log.name and log.name=%s
|
||||||
|
""", logname)
|
||||||
|
|
||||||
|
return flt(expense_amount[0][0]) if expense_amount else 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def get_chart_data(data, filters):
|
||||||
|
period_list = get_period_list(filters.fiscal_year, filters.fiscal_year,
|
||||||
|
filters.from_date, filters.to_date, filters.filter_based_on, 'Monthly')
|
||||||
|
|
||||||
|
fuel_data, service_data = [], []
|
||||||
|
|
||||||
def get_chart_data(data,period_list):
|
|
||||||
fuel_exp_data,service_exp_data,fueldata,servicedata = [],[],[],[]
|
|
||||||
service_exp_data = []
|
|
||||||
fueldata = []
|
|
||||||
for period in period_list:
|
for period in period_list:
|
||||||
total_fuel_exp = 0
|
total_fuel_exp = 0
|
||||||
total_ser_exp=0
|
total_service_exp = 0
|
||||||
for row in data:
|
|
||||||
if row["Date"] <= period.to_date and row["Date"] >= period.from_date:
|
for row in data:
|
||||||
total_fuel_exp+=flt(row["Fuel Expense"])
|
if row.date <= period.to_date and row.date >= period.from_date:
|
||||||
total_ser_exp+=flt(row["Service Expense"])
|
total_fuel_exp += flt(row.fuel_expense)
|
||||||
fueldata.append([period.key,total_fuel_exp])
|
total_service_exp += flt(row.service_expense)
|
||||||
servicedata.append([period.key,total_ser_exp])
|
|
||||||
|
fuel_data.append([period.key, total_fuel_exp])
|
||||||
|
service_data.append([period.key, total_service_exp])
|
||||||
|
|
||||||
|
labels = [period.label for period in period_list]
|
||||||
|
fuel_exp_data= [row[1] for row in fuel_data]
|
||||||
|
service_exp_data= [row[1] for row in service_data]
|
||||||
|
|
||||||
labels = [period.key for period in period_list]
|
|
||||||
fuel_exp_data= [row[1] for row in fueldata]
|
|
||||||
service_exp_data= [row[1] for row in servicedata]
|
|
||||||
datasets = []
|
datasets = []
|
||||||
if fuel_exp_data:
|
if fuel_exp_data:
|
||||||
datasets.append({
|
datasets.append({
|
||||||
'name': 'Fuel Expenses',
|
'name': _('Fuel Expenses'),
|
||||||
'values': fuel_exp_data
|
'values': fuel_exp_data
|
||||||
})
|
})
|
||||||
|
|
||||||
if service_exp_data:
|
if service_exp_data:
|
||||||
datasets.append({
|
datasets.append({
|
||||||
'name': 'Service Expenses',
|
'name': _('Service Expenses'),
|
||||||
'values': service_exp_data
|
'values': service_exp_data
|
||||||
})
|
})
|
||||||
|
|
||||||
chart = {
|
chart = {
|
||||||
"data": {
|
'data': {
|
||||||
'labels': labels,
|
'labels': labels,
|
||||||
'datasets': datasets
|
'datasets': datasets
|
||||||
|
},
|
||||||
|
'type': 'line',
|
||||||
|
'fieldtype': 'Currency'
|
||||||
}
|
}
|
||||||
}
|
|
||||||
chart["type"] = "line"
|
|
||||||
return chart
|
return chart
|
||||||
|
@ -269,6 +269,7 @@ def get_total_exemption_amount(declarations):
|
|||||||
total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()])
|
total_exemption_amount = sum([flt(d.total_exemption_amount) for d in exemptions.values()])
|
||||||
return total_exemption_amount
|
return total_exemption_amount
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_leave_period(from_date, to_date, company):
|
def get_leave_period(from_date, to_date, company):
|
||||||
leave_period = frappe.db.sql("""
|
leave_period = frappe.db.sql("""
|
||||||
select name, from_date, to_date
|
select name, from_date, to_date
|
||||||
|
@ -264,7 +264,7 @@ def make_loan_write_off(loan, company=None, posting_date=None, amount=0, as_dict
|
|||||||
pending_amount = amounts['pending_principal_amount']
|
pending_amount = amounts['pending_principal_amount']
|
||||||
|
|
||||||
if amount and (amount > pending_amount):
|
if amount and (amount > pending_amount):
|
||||||
frappe.throw('Write Off amount cannot be greater than pending loan amount')
|
frappe.throw(_('Write Off amount cannot be greater than pending loan amount'))
|
||||||
|
|
||||||
if not amount:
|
if not amount:
|
||||||
amount = pending_amount
|
amount = pending_amount
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_default": 0,
|
"is_default": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Loan Management",
|
"label": "Loans",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -220,10 +220,10 @@
|
|||||||
"type": "Link"
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-02-18 17:31:53.586508",
|
"modified": "2021-05-25 17:31:53.586508",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Loan Management",
|
"module": "Loan Management",
|
||||||
"name": "Loan Management",
|
"name": "Loans",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"pin_to_bottom": 0,
|
"pin_to_bottom": 0,
|
||||||
"pin_to_top": 0,
|
"pin_to_top": 0,
|
||||||
|
@ -2,15 +2,11 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.provide("erpnext.maintenance");
|
frappe.provide("erpnext.maintenance");
|
||||||
|
|
||||||
frappe.ui.form.on('Maintenance Schedule', {
|
frappe.ui.form.on('Maintenance Schedule', {
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
frm.set_query('contact_person', erpnext.queries.contact_query);
|
frm.set_query('contact_person', erpnext.queries.contact_query);
|
||||||
frm.set_query('customer_address', erpnext.queries.address_query);
|
frm.set_query('customer_address', erpnext.queries.address_query);
|
||||||
frm.set_query('customer', erpnext.queries.customer);
|
frm.set_query('customer', erpnext.queries.customer);
|
||||||
|
|
||||||
frm.add_fetch('item_code', 'item_name', 'item_name');
|
|
||||||
frm.add_fetch('item_code', 'description', 'description');
|
|
||||||
},
|
},
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
if (!frm.doc.status) {
|
if (!frm.doc.status) {
|
||||||
@ -47,7 +43,7 @@ frappe.ui.form.on('Maintenance Schedule', {
|
|||||||
// TODO commonify this code
|
// TODO commonify this code
|
||||||
erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
||||||
refresh: function () {
|
refresh: function () {
|
||||||
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer' };
|
||||||
|
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
@ -68,14 +64,80 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
|||||||
});
|
});
|
||||||
}, __("Get Items From"));
|
}, __("Get Items From"));
|
||||||
} else if (this.frm.doc.docstatus === 1) {
|
} else if (this.frm.doc.docstatus === 1) {
|
||||||
this.frm.add_custom_button(__('Create Maintenance Visit'), function() {
|
let schedules = me.frm.doc.schedules;
|
||||||
frappe.model.open_mapped_doc({
|
let flag = schedules.some(schedule => schedule.completion_status === "Pending");
|
||||||
|
if (flag) {
|
||||||
|
this.frm.add_custom_button(__('Maintenance Visit'), function () {
|
||||||
|
let options = "";
|
||||||
|
|
||||||
|
me.frm.call('get_pending_data', {data_type: "items"}).then(r => {
|
||||||
|
options = r.message;
|
||||||
|
|
||||||
|
let schedule_id = "";
|
||||||
|
let d = new frappe.ui.Dialog({
|
||||||
|
title: __("Enter Visit Details"),
|
||||||
|
fields: [{
|
||||||
|
fieldtype: "Select",
|
||||||
|
fieldname: "item_name",
|
||||||
|
label: __("Item Name"),
|
||||||
|
options: options,
|
||||||
|
reqd: 1,
|
||||||
|
onchange: function () {
|
||||||
|
let field = d.get_field("scheduled_date");
|
||||||
|
me.frm.call('get_pending_data',
|
||||||
|
{
|
||||||
|
item_name: this.value,
|
||||||
|
data_type: "date"
|
||||||
|
}).then(r => {
|
||||||
|
field.df.options = r.message;
|
||||||
|
field.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: __('Scheduled Date'),
|
||||||
|
fieldname: 'scheduled_date',
|
||||||
|
fieldtype: 'Select',
|
||||||
|
options: "",
|
||||||
|
reqd: 1,
|
||||||
|
onchange: function () {
|
||||||
|
let field = d.get_field('item_name');
|
||||||
|
me.frm.call(
|
||||||
|
'get_pending_data',
|
||||||
|
{
|
||||||
|
item_name: field.value,
|
||||||
|
s_date: this.value,
|
||||||
|
data_type: "id"
|
||||||
|
}).then(r => {
|
||||||
|
schedule_id = r.message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primary_action_label: 'Create Visit',
|
||||||
|
primary_action(values) {
|
||||||
|
frappe.call({
|
||||||
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit",
|
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit",
|
||||||
|
args: {
|
||||||
|
item_name: values.item_name,
|
||||||
|
s_id: schedule_id,
|
||||||
source_name: me.frm.doc.name,
|
source_name: me.frm.doc.name,
|
||||||
frm: me.frm
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (!r.exc) {
|
||||||
|
frappe.model.sync(r.message);
|
||||||
|
frappe.set_route("Form", r.message.doctype, r.message.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
d.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
d.show();
|
||||||
});
|
});
|
||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
start_date: function (doc, cdt, cdn) {
|
start_date: function (doc, cdt, cdn) {
|
||||||
@ -88,29 +150,18 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
|||||||
|
|
||||||
periodicity: function (doc, cdt, cdn) {
|
periodicity: function (doc, cdt, cdn) {
|
||||||
this.set_no_of_visits(doc, cdt, cdn);
|
this.set_no_of_visits(doc, cdt, cdn);
|
||||||
|
|
||||||
|
},
|
||||||
|
no_of_visits: function (doc, cdt, cdn) {
|
||||||
|
this.set_no_of_visits(doc, cdt, cdn);
|
||||||
},
|
},
|
||||||
|
|
||||||
set_no_of_visits: function (doc, cdt, cdn) {
|
set_no_of_visits: function (doc, cdt, cdn) {
|
||||||
var item = frappe.get_doc(cdt, cdn);
|
var item = frappe.get_doc(cdt, cdn);
|
||||||
|
let me = this;
|
||||||
|
if (item.start_date && item.periodicity) {
|
||||||
|
me.frm.call('validate_end_date_visits');
|
||||||
|
|
||||||
if (item.start_date && item.end_date && item.periodicity) {
|
|
||||||
if(item.start_date > item.end_date) {
|
|
||||||
frappe.msgprint(__("Row {0}:Start Date must be before End Date", [item.idx]));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var date_diff = frappe.datetime.get_diff(item.end_date, item.start_date) + 1;
|
|
||||||
|
|
||||||
var days_in_period = {
|
|
||||||
"Weekly": 7,
|
|
||||||
"Monthly": 30,
|
|
||||||
"Quarterly": 91,
|
|
||||||
"Half Yearly": 182,
|
|
||||||
"Yearly": 365
|
|
||||||
}
|
|
||||||
|
|
||||||
var no_of_visits = cint(date_diff / days_in_period[item.periodicity]);
|
|
||||||
frappe.model.set_value(item.doctype, item.name, "no_of_visits", no_of_visits);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1,819 +1,242 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-01-10 16:34:30",
|
"creation": "2013-01-10 16:34:30",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 0,
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"customer_details",
|
||||||
|
"naming_series",
|
||||||
|
"customer",
|
||||||
|
"column_break0",
|
||||||
|
"status",
|
||||||
|
"transaction_date",
|
||||||
|
"items_section",
|
||||||
|
"items",
|
||||||
|
"schedule",
|
||||||
|
"generate_schedule",
|
||||||
|
"schedules",
|
||||||
|
"contact_info",
|
||||||
|
"customer_name",
|
||||||
|
"contact_person",
|
||||||
|
"contact_mobile",
|
||||||
|
"contact_email",
|
||||||
|
"contact_display",
|
||||||
|
"column_break_17",
|
||||||
|
"customer_address",
|
||||||
|
"address_display",
|
||||||
|
"territory",
|
||||||
|
"customer_group",
|
||||||
|
"company",
|
||||||
|
"amended_from"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "customer_details",
|
"fieldname": "customer_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "fa fa-user",
|
"options": "fa fa-user"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Series",
|
"label": "Series",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "MAT-MSH-.YYYY.-",
|
"options": "MAT-MSH-.YYYY.-",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"set_only_once": 1
|
||||||
"set_only_once": 1,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "customer",
|
"fieldname": "customer",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Customer",
|
"label": "Customer",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "customer",
|
"oldfieldname": "customer",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Customer",
|
"options": "Customer",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"search_index": 1
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break0",
|
"fieldname": "column_break0",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"oldfieldtype": "Column Break"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Column Break",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Draft",
|
"default": "Draft",
|
||||||
"fieldname": "status",
|
"fieldname": "status",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Status",
|
"label": "Status",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nDraft\nSubmitted\nCancelled",
|
"options": "\nDraft\nSubmitted\nCancelled",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"reqd": 1
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "transaction_date",
|
"fieldname": "transaction_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Transaction Date",
|
"label": "Transaction Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "transaction_date",
|
"oldfieldname": "transaction_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "items_section",
|
"fieldname": "items_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "fa fa-shopping-cart",
|
"options": "fa fa-shopping-cart"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "items",
|
"fieldname": "items",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Items",
|
"label": "Items",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_maintenance_detail",
|
"oldfieldname": "item_maintenance_detail",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Maintenance Schedule Item",
|
"options": "Maintenance Schedule Item",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "schedule",
|
"fieldname": "schedule",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Schedule",
|
"label": "Schedule",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Section Break",
|
"oldfieldtype": "Section Break",
|
||||||
"options": "fa fa-time",
|
"options": "fa fa-time"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "generate_schedule",
|
"fieldname": "generate_schedule",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Generate Schedule",
|
"label": "Generate Schedule",
|
||||||
"length": 0,
|
"oldfieldtype": "Button"
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldtype": "Button",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "schedules",
|
"fieldname": "schedules",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Schedules",
|
"label": "Schedules",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "schedules",
|
"oldfieldname": "schedules",
|
||||||
"oldfieldtype": "Table",
|
"oldfieldtype": "Table",
|
||||||
"options": "Maintenance Schedule Detail",
|
"options": "Maintenance Schedule Detail"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "contact_info",
|
"fieldname": "contact_info",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Contact Info"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact Info",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 1,
|
"bold": 1,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"fieldname": "customer_name",
|
"fieldname": "customer_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Customer Name",
|
"label": "Customer Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "customer_name",
|
"oldfieldname": "customer_name",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"fieldname": "contact_person",
|
"fieldname": "contact_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact Person",
|
"label": "Contact Person",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Contact",
|
"options": "Contact",
|
||||||
"permlevel": 0,
|
"print_hide": 1
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"fieldname": "contact_mobile",
|
"fieldname": "contact_mobile",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Mobile No",
|
"label": "Mobile No",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"fieldname": "contact_email",
|
"fieldname": "contact_email",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact Email",
|
"label": "Contact Email",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "contact_display",
|
"fieldname": "contact_display",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Contact",
|
"label": "Contact",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_17",
|
"fieldname": "column_break_17",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"fieldname": "customer_address",
|
"fieldname": "customer_address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Customer Address",
|
"label": "Customer Address",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Address",
|
"options": "Address",
|
||||||
"permlevel": 0,
|
"print_hide": 1
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "address_display",
|
"fieldname": "address_display",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Address",
|
"label": "Address",
|
||||||
"length": 0,
|
"read_only": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"description": "",
|
|
||||||
"fieldname": "territory",
|
"fieldname": "territory",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Territory",
|
"label": "Territory",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "territory",
|
"oldfieldname": "territory",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Territory",
|
"options": "Territory"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "customer",
|
"depends_on": "customer",
|
||||||
"description": "",
|
|
||||||
"fieldname": "customer_group",
|
"fieldname": "customer_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Customer Group",
|
"label": "Customer Group",
|
||||||
"length": 0,
|
"options": "Customer Group"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Customer Group",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "company",
|
"oldfieldname": "company",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
"remember_last_selected_value": 1,
|
||||||
"report_hide": 0,
|
"reqd": 1
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Amended From",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Maintenance Schedule",
|
"options": "Maintenance Schedule",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"links": [
|
||||||
"istable": 0,
|
{
|
||||||
"max_attachments": 0,
|
"group": "Visits",
|
||||||
"modified": "2020-09-18 17:26:09.703215",
|
"link_doctype": "Maintenance Visit",
|
||||||
|
"link_fieldname": "maintenance_schedule"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"modified": "2021-05-27 16:05:10.746465",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Maintenance",
|
"module": "Maintenance",
|
||||||
"name": "Maintenance Schedule",
|
"name": "Maintenance Schedule",
|
||||||
@ -825,28 +248,17 @@
|
|||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 0,
|
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Maintenance Manager",
|
"role": "Maintenance Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"search_fields": "status,customer,customer_name",
|
"search_fields": "status,customer,customer_name",
|
||||||
"show_name_in_global_search": 0,
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"timeline_field": "customer",
|
"timeline_field": "customer"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -4,12 +4,13 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
from frappe.utils import add_days, getdate, cint, cstr
|
from frappe.utils import add_days, getdate, cint, cstr, date_diff, formatdate
|
||||||
|
|
||||||
from frappe import throw, _
|
from frappe import throw, _
|
||||||
from erpnext.utilities.transaction_base import TransactionBase, delete_events
|
from erpnext.utilities.transaction_base import TransactionBase, delete_events
|
||||||
from erpnext.stock.utils import get_valid_serial_nos
|
from erpnext.stock.utils import get_valid_serial_nos
|
||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||||
|
|
||||||
class MaintenanceSchedule(TransactionBase):
|
class MaintenanceSchedule(TransactionBase):
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@ -32,8 +33,40 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
child.idx = count
|
child.idx = count
|
||||||
count = count + 1
|
count = count + 1
|
||||||
child.sales_person = d.sales_person
|
child.sales_person = d.sales_person
|
||||||
|
child.completion_status = "Pending"
|
||||||
|
child.item_reference = d.name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def validate_end_date_visits(self):
|
||||||
|
days_in_period = {
|
||||||
|
"Weekly": 7,
|
||||||
|
"Monthly": 30,
|
||||||
|
"Quarterly": 91,
|
||||||
|
"Half Yearly": 182,
|
||||||
|
"Yearly": 365
|
||||||
|
}
|
||||||
|
for item in self.items:
|
||||||
|
if item.periodicity and item.start_date:
|
||||||
|
if not item.end_date:
|
||||||
|
if item.no_of_visits:
|
||||||
|
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
|
||||||
|
else:
|
||||||
|
item.end_date = add_days(item.start_date, days_in_period[item.periodicity])
|
||||||
|
|
||||||
|
diff = date_diff(item.end_date, item.start_date) + 1
|
||||||
|
no_of_visits = cint(diff / days_in_period[item.periodicity])
|
||||||
|
|
||||||
|
if not item.no_of_visits or item.no_of_visits == 0:
|
||||||
|
item.end_date = add_days(item.start_date, days_in_period[item.periodicity])
|
||||||
|
diff = date_diff(item.end_date, item.start_date) + 1
|
||||||
|
item.no_of_visits = cint(diff / days_in_period[item.periodicity])
|
||||||
|
|
||||||
|
elif item.no_of_visits > no_of_visits:
|
||||||
|
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
|
||||||
|
|
||||||
|
elif item.no_of_visits < no_of_visits:
|
||||||
|
item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity])
|
||||||
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if not self.get('schedules'):
|
if not self.get('schedules'):
|
||||||
@ -58,9 +91,10 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
|
|
||||||
if no_email_sp:
|
if no_email_sp:
|
||||||
frappe.msgprint(
|
frappe.msgprint(
|
||||||
frappe._("Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}").format(
|
_("Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}").format(
|
||||||
self.owner, "<br>" + "<br>".join(no_email_sp)
|
self.owner, "<br>" + "<br>".join(no_email_sp)
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
|
||||||
scheduled_date = frappe.db.sql("""select scheduled_date from
|
scheduled_date = frappe.db.sql("""select scheduled_date from
|
||||||
`tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and
|
`tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and
|
||||||
@ -135,8 +169,7 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if date_diff < days_in_period[d.periodicity]:
|
if date_diff < days_in_period[d.periodicity]:
|
||||||
throw(_("Row {0}: To set {1} periodicity, difference between from and to date \
|
throw(_("Row {0}: To set {1} periodicity, difference between from and to date must be greater than or equal to {2}")
|
||||||
must be greater than or equal to {2}")
|
|
||||||
.format(d.idx, d.periodicity, days_in_period[d.periodicity]))
|
.format(d.idx, d.periodicity, days_in_period[d.periodicity]))
|
||||||
|
|
||||||
def validate_maintenance_detail(self):
|
def validate_maintenance_detail(self):
|
||||||
@ -166,9 +199,11 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
|
throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_end_date_visits()
|
||||||
self.validate_maintenance_detail()
|
self.validate_maintenance_detail()
|
||||||
self.validate_dates_with_periodicity()
|
self.validate_dates_with_periodicity()
|
||||||
self.validate_sales_order()
|
self.validate_sales_order()
|
||||||
|
self.generate_schedule()
|
||||||
|
|
||||||
def on_update(self):
|
def on_update(self):
|
||||||
frappe.db.set(self, 'status', 'Draft')
|
frappe.db.set(self, 'status', 'Draft')
|
||||||
@ -246,11 +281,48 @@ class MaintenanceSchedule(TransactionBase):
|
|||||||
delete_events(self.doctype, self.name)
|
delete_events(self.doctype, self.name)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_maintenance_visit(source_name, target_doc=None):
|
def get_pending_data(self, data_type, s_date=None, item_name=None):
|
||||||
|
if data_type == "date":
|
||||||
|
dates = ""
|
||||||
|
for schedule in self.schedules:
|
||||||
|
if schedule.item_name == item_name and schedule.completion_status == "Pending":
|
||||||
|
dates = dates + "\n" + formatdate(schedule.scheduled_date, "dd-MM-yyyy")
|
||||||
|
return dates
|
||||||
|
elif data_type == "items":
|
||||||
|
items = ""
|
||||||
|
for item in self.items:
|
||||||
|
for schedule in self.schedules:
|
||||||
|
if item.item_name == schedule.item_name and schedule.completion_status == "Pending":
|
||||||
|
items = items + "\n" + item.item_name
|
||||||
|
break
|
||||||
|
return items
|
||||||
|
elif data_type == "id":
|
||||||
|
for schedule in self.schedules:
|
||||||
|
if schedule.item_name == item_name and s_date == formatdate(schedule.scheduled_date, "dd-mm-yyyy"):
|
||||||
|
return schedule.name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def update_serial_nos(s_id):
|
||||||
|
serial_nos = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'serial_no')
|
||||||
|
if serial_nos:
|
||||||
|
serial_nos = get_serial_nos(serial_nos)
|
||||||
|
return serial_nos
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
def update_status(source, target, parent):
|
def update_status_and_detail(source, target, parent):
|
||||||
target.maintenance_type = "Scheduled"
|
target.maintenance_type = "Scheduled"
|
||||||
|
target.maintenance_schedule = source.name
|
||||||
|
target.maintenance_schedule_detail = s_id
|
||||||
|
|
||||||
|
def update_sales(source, target, parent):
|
||||||
|
sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person')
|
||||||
|
target.service_person = sales_person
|
||||||
|
target.serial_no = ''
|
||||||
|
|
||||||
doclist = get_mapped_doc("Maintenance Schedule", source_name, {
|
doclist = get_mapped_doc("Maintenance Schedule", source_name, {
|
||||||
"Maintenance Schedule": {
|
"Maintenance Schedule": {
|
||||||
@ -261,15 +333,12 @@ def make_maintenance_visit(source_name, target_doc=None):
|
|||||||
"validation": {
|
"validation": {
|
||||||
"docstatus": ["=", 1]
|
"docstatus": ["=", 1]
|
||||||
},
|
},
|
||||||
"postprocess": update_status
|
"postprocess": update_status_and_detail
|
||||||
},
|
},
|
||||||
"Maintenance Schedule Item": {
|
"Maintenance Schedule Item": {
|
||||||
"doctype": "Maintenance Visit Purpose",
|
"doctype": "Maintenance Visit Purpose",
|
||||||
"field_map": {
|
"condition": lambda doc: doc.item_name == item_name,
|
||||||
"parent": "prevdoc_docname",
|
"postprocess": update_sales
|
||||||
"parenttype": "prevdoc_doctype",
|
|
||||||
"sales_person": "service_person"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, target_doc)
|
}, target_doc)
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
from frappe.utils.data import get_datetime, add_days
|
from frappe.utils.data import add_days, today, formatdate
|
||||||
|
from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import make_maintenance_visit
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
@ -22,6 +23,56 @@ class TestMaintenanceSchedule(unittest.TestCase):
|
|||||||
events_after_cancel = get_events(ms)
|
events_after_cancel = get_events(ms)
|
||||||
self.assertTrue(len(events_after_cancel) == 0)
|
self.assertTrue(len(events_after_cancel) == 0)
|
||||||
|
|
||||||
|
def test_make_schedule(self):
|
||||||
|
ms = make_maintenance_schedule()
|
||||||
|
ms.save()
|
||||||
|
i = ms.items[0]
|
||||||
|
expected_dates = []
|
||||||
|
expected_end_date = add_days(i.start_date, i.no_of_visits * 7)
|
||||||
|
self.assertEqual(i.end_date, expected_end_date)
|
||||||
|
|
||||||
|
i.no_of_visits = 2
|
||||||
|
ms.save()
|
||||||
|
expected_end_date = add_days(i.start_date, i.no_of_visits * 7)
|
||||||
|
self.assertEqual(i.end_date, expected_end_date)
|
||||||
|
|
||||||
|
items = ms.get_pending_data(data_type = "items")
|
||||||
|
items = items.split('\n')
|
||||||
|
items.pop(0)
|
||||||
|
expected_items = ['_Test Item']
|
||||||
|
self.assertTrue(items, expected_items)
|
||||||
|
|
||||||
|
# "dates" contains all generated schedule dates
|
||||||
|
dates = ms.get_pending_data(data_type = "date", item_name = i.item_name)
|
||||||
|
dates = dates.split('\n')
|
||||||
|
dates.pop(0)
|
||||||
|
expected_dates.append(formatdate(add_days(i.start_date, 7), "dd-MM-yyyy"))
|
||||||
|
expected_dates.append(formatdate(add_days(i.start_date, 14), "dd-MM-yyyy"))
|
||||||
|
|
||||||
|
# test for generated schedule dates
|
||||||
|
self.assertEqual(dates, expected_dates)
|
||||||
|
|
||||||
|
ms.submit()
|
||||||
|
s_id = ms.get_pending_data(data_type = "id", item_name = i.item_name, s_date = expected_dates[1])
|
||||||
|
test = make_maintenance_visit(source_name = ms.name, item_name = "_Test Item", s_id = s_id)
|
||||||
|
visit = frappe.new_doc('Maintenance Visit')
|
||||||
|
visit = test
|
||||||
|
visit.maintenance_schedule = ms.name
|
||||||
|
visit.maintenance_schedule_detail = s_id
|
||||||
|
visit.completion_status = "Partially Completed"
|
||||||
|
visit.set('purposes', [{
|
||||||
|
'item_code': i.item_code,
|
||||||
|
'description': "test",
|
||||||
|
'work_done': "test",
|
||||||
|
'service_person': "Sales Team",
|
||||||
|
}])
|
||||||
|
visit.save()
|
||||||
|
visit.submit()
|
||||||
|
ms = frappe.get_doc('Maintenance Schedule', ms.name)
|
||||||
|
|
||||||
|
#checks if visit status is back updated in schedule
|
||||||
|
self.assertTrue(ms.schedules[1].completion_status, "Partially Completed")
|
||||||
|
|
||||||
def get_events(ms):
|
def get_events(ms):
|
||||||
return frappe.get_all("Event Participants", filters={
|
return frappe.get_all("Event Participants", filters={
|
||||||
"reference_doctype": ms.doctype,
|
"reference_doctype": ms.doctype,
|
||||||
@ -33,12 +84,11 @@ def make_maintenance_schedule():
|
|||||||
ms = frappe.new_doc("Maintenance Schedule")
|
ms = frappe.new_doc("Maintenance Schedule")
|
||||||
ms.company = "_Test Company"
|
ms.company = "_Test Company"
|
||||||
ms.customer = "_Test Customer"
|
ms.customer = "_Test Customer"
|
||||||
ms.transaction_date = get_datetime()
|
ms.transaction_date = today()
|
||||||
|
|
||||||
ms.append("items", {
|
ms.append("items", {
|
||||||
"item_code": "_Test Item",
|
"item_code": "_Test Item",
|
||||||
"start_date": get_datetime(),
|
"start_date": today(),
|
||||||
"end_date": add_days(get_datetime(), 32),
|
|
||||||
"periodicity": "Weekly",
|
"periodicity": "Weekly",
|
||||||
"no_of_visits": 4,
|
"no_of_visits": 4,
|
||||||
"sales_person": "Sales Team",
|
"sales_person": "Sales Team",
|
||||||
|
@ -1,222 +1,137 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-02-22 01:28:05",
|
"creation": "2013-02-22 01:28:05",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"item_code",
|
||||||
|
"item_name",
|
||||||
|
"column_break_3",
|
||||||
|
"scheduled_date",
|
||||||
|
"actual_date",
|
||||||
|
"section_break_6",
|
||||||
|
"sales_person",
|
||||||
|
"column_break_8",
|
||||||
|
"completion_status",
|
||||||
|
"section_break_10",
|
||||||
|
"serial_no",
|
||||||
|
"item_reference"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"columns": 2,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_code",
|
"oldfieldname": "item_code",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"search_index": 1
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 1,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Name",
|
"label": "Item Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_name",
|
"oldfieldname": "item_name",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"columns": 2,
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "scheduled_date",
|
"fieldname": "scheduled_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Scheduled Date",
|
"label": "Scheduled Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "scheduled_date",
|
"oldfieldname": "scheduled_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "actual_date",
|
"fieldname": "actual_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 1,
|
"in_list_view": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Actual Date",
|
"label": "Actual Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "actual_date",
|
"oldfieldname": "actual_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1,
|
||||||
"read_only": 0,
|
"report_hide": 1
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 1,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
"allow_on_submit": 1,
|
||||||
"bold": 0,
|
"columns": 2,
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sales_person",
|
"fieldname": "sales_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sales Person",
|
"label": "Sales Person",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "incharge_name",
|
"oldfieldname": "incharge_name",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Sales Person",
|
"options": "Sales Person",
|
||||||
"permlevel": 0,
|
"read_only_depends_on": "eval:doc.completion_status != \"Pending\""
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Serial No",
|
"label": "Serial No",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "serial_no",
|
"oldfieldname": "serial_no",
|
||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "160px",
|
"print_width": "160px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "160px"
|
"width": "160px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "completion_status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Completion Status",
|
||||||
|
"options": "Pending\nPartially Completed\nFully Completed",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_8",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_10",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "item_reference",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Item Reference",
|
||||||
|
"options": "Maintenance Schedule Item",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2017-02-17 17:05:44.644663",
|
"modified": "2021-05-27 16:07:25.905015",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Maintenance",
|
"module": "Maintenance",
|
||||||
"name": "Maintenance Schedule Detail",
|
"name": "Maintenance Schedule Detail",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC",
|
||||||
"read_only_onload": 0,
|
"track_changes": 1
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -1,431 +1,160 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-02-22 01:28:05",
|
"creation": "2013-02-22 01:28:05",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"item_code",
|
||||||
|
"item_name",
|
||||||
|
"description",
|
||||||
|
"column_break_4",
|
||||||
|
"start_date",
|
||||||
|
"end_date",
|
||||||
|
"periodicity",
|
||||||
|
"schedule_details",
|
||||||
|
"no_of_visits",
|
||||||
|
"column_break_10",
|
||||||
|
"sales_person",
|
||||||
|
"reference",
|
||||||
|
"serial_no",
|
||||||
|
"sales_order"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_code",
|
"oldfieldname": "item_code",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 1,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "item_code.item_name",
|
"fetch_from": "item_code.item_name",
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Name",
|
"label": "Item Name",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_name",
|
"oldfieldname": "item_name",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "",
|
"read_only": 1
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_from": "item_code.description",
|
"fetch_from": "item_code.description",
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "description",
|
"oldfieldname": "description",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "300px",
|
"print_width": "300px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "300px"
|
"width": "300px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "schedule_details",
|
"fieldname": "schedule_details",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "start_date",
|
"fieldname": "start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Start Date",
|
"label": "Start Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "start_date",
|
"oldfieldname": "start_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "end_date",
|
"fieldname": "end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "End Date",
|
"label": "End Date",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "end_date",
|
"oldfieldname": "end_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1,
|
"search_index": 1
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 1,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "periodicity",
|
"fieldname": "periodicity",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Periodicity",
|
"label": "Periodicity",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "periodicity",
|
"oldfieldname": "periodicity",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nWeekly\nMonthly\nQuarterly\nHalf Yearly\nYearly\nRandom",
|
"options": "\nWeekly\nMonthly\nQuarterly\nHalf Yearly\nYearly\nRandom"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 1,
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "no_of_visits",
|
"fieldname": "no_of_visits",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Int",
|
||||||
"hidden": 0,
|
"in_list_view": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "No of Visits",
|
"label": "No of Visits",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "no_of_visits",
|
"oldfieldname": "no_of_visits",
|
||||||
"oldfieldtype": "Int",
|
"oldfieldtype": "Int",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sales_person",
|
"fieldname": "sales_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sales Person",
|
"label": "Sales Person",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "incharge_name",
|
"oldfieldname": "incharge_name",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Sales Person",
|
"options": "Sales Person"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "reference",
|
"fieldname": "reference",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Reference"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Reference",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Serial No",
|
"label": "Serial No",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "serial_no",
|
"oldfieldname": "serial_no",
|
||||||
"oldfieldtype": "Small Text",
|
"oldfieldtype": "Small Text"
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sales_order",
|
"fieldname": "sales_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sales Order",
|
"label": "Sales Order",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "prevdoc_docname",
|
"oldfieldname": "prevdoc_docname",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "Sales Order",
|
"options": "Sales Order",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 1,
|
"search_index": 1,
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_4",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_10",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2018-05-16 22:43:14.260729",
|
"modified": "2021-04-15 16:09:47.311994",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Maintenance",
|
"module": "Maintenance",
|
||||||
"name": "Maintenance Schedule Item",
|
"name": "Maintenance Schedule Item",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "DESC"
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -2,17 +2,26 @@
|
|||||||
// License: GNU General Public License v3. See license.txt
|
// License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
frappe.provide("erpnext.maintenance");
|
frappe.provide("erpnext.maintenance");
|
||||||
|
var serial_nos = [];
|
||||||
frappe.ui.form.on('Maintenance Visit', {
|
frappe.ui.form.on('Maintenance Visit', {
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
//filters for serial_no based on item_code
|
//filters for serial_no based on item_code
|
||||||
frm.set_query('serial_no', 'purposes', function (frm, cdt, cdn) {
|
frm.set_query('serial_no', 'purposes', function (frm, cdt, cdn) {
|
||||||
let item = locals[cdt][cdn];
|
let item = locals[cdt][cdn];
|
||||||
|
if (serial_nos) {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'item_code': item.item_code,
|
||||||
|
'name': ["in", serial_nos]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
'item_code': item.item_code
|
'item_code': item.item_code
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
setup: function (frm) {
|
setup: function (frm) {
|
||||||
@ -20,7 +29,21 @@ frappe.ui.form.on('Maintenance Visit', {
|
|||||||
frm.set_query('customer_address', erpnext.queries.address_query);
|
frm.set_query('customer_address', erpnext.queries.address_query);
|
||||||
frm.set_query('customer', erpnext.queries.customer);
|
frm.set_query('customer', erpnext.queries.customer);
|
||||||
},
|
},
|
||||||
onload: function(frm) {
|
onload: function (frm, cdt, cdn) {
|
||||||
|
let item = locals[cdt][cdn];
|
||||||
|
if (frm.maintenance_type == 'Scheduled') {
|
||||||
|
let schedule_id = item.purposes[0].prevdoc_detail_docname;
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.update_serial_nos",
|
||||||
|
args: {
|
||||||
|
s_id: schedule_id
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
serial_nos = r.message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!frm.doc.status) {
|
if (!frm.doc.status) {
|
||||||
frm.set_value({ status: 'Draft' });
|
frm.set_value({ status: 'Draft' });
|
||||||
}
|
}
|
||||||
@ -43,7 +66,7 @@ frappe.ui.form.on('Maintenance Visit', {
|
|||||||
// TODO commonify this code
|
// TODO commonify this code
|
||||||
erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
|
erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
|
||||||
refresh: function () {
|
refresh: function () {
|
||||||
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer' };
|
||||||
|
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
|
from frappe.utils import get_datetime
|
||||||
|
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
@ -16,10 +17,28 @@ class MaintenanceVisit(TransactionBase):
|
|||||||
if d.serial_no and not frappe.db.exists("Serial No", d.serial_no):
|
if d.serial_no and not frappe.db.exists("Serial No", d.serial_no):
|
||||||
frappe.throw(_("Serial No {0} does not exist").format(d.serial_no))
|
frappe.throw(_("Serial No {0} does not exist").format(d.serial_no))
|
||||||
|
|
||||||
|
def validate_maintenance_date(self):
|
||||||
|
if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail:
|
||||||
|
item_ref = frappe.db.get_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'item_reference')
|
||||||
|
if item_ref:
|
||||||
|
start_date, end_date = frappe.db.get_value('Maintenance Schedule Item', item_ref, ['start_date', 'end_date'])
|
||||||
|
if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime(self.mntc_date) > get_datetime(end_date):
|
||||||
|
frappe.throw(_("Date must be between {0} and {1}").format(start_date, end_date))
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_serial_no()
|
self.validate_serial_no()
|
||||||
|
self.validate_maintenance_date()
|
||||||
|
|
||||||
|
def update_completion_status(self):
|
||||||
|
if self.maintenance_schedule_detail:
|
||||||
|
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', self.completion_status)
|
||||||
|
|
||||||
|
def update_actual_date(self):
|
||||||
|
if self.maintenance_schedule_detail:
|
||||||
|
frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', self.mntc_date)
|
||||||
|
|
||||||
def update_customer_issue(self, flag):
|
def update_customer_issue(self, flag):
|
||||||
|
if not self.maintenance_schedule:
|
||||||
for d in self.get('purposes'):
|
for d in self.get('purposes'):
|
||||||
if d.prevdoc_docname and d.prevdoc_doctype == 'Warranty Claim' :
|
if d.prevdoc_docname and d.prevdoc_doctype == 'Warranty Claim' :
|
||||||
if flag==1:
|
if flag==1:
|
||||||
@ -77,6 +96,8 @@ class MaintenanceVisit(TransactionBase):
|
|||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.update_customer_issue(1)
|
self.update_customer_issue(1)
|
||||||
frappe.db.set(self, 'status', 'Submitted')
|
frappe.db.set(self, 'status', 'Submitted')
|
||||||
|
self.update_completion_status()
|
||||||
|
self.update_actual_date()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.check_if_last_visit()
|
self.check_if_last_visit()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"creation": "2013-02-22 01:28:06",
|
"creation": "2013-02-22 01:28:06",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -8,14 +9,15 @@
|
|||||||
"field_order": [
|
"field_order": [
|
||||||
"item_code",
|
"item_code",
|
||||||
"item_name",
|
"item_name",
|
||||||
|
"column_break_3",
|
||||||
|
"service_person",
|
||||||
"serial_no",
|
"serial_no",
|
||||||
|
"section_break_6",
|
||||||
"description",
|
"description",
|
||||||
"work_details",
|
"work_details",
|
||||||
"service_person",
|
|
||||||
"work_done",
|
"work_done",
|
||||||
"prevdoc_doctype",
|
"prevdoc_doctype",
|
||||||
"prevdoc_docname",
|
"prevdoc_docname"
|
||||||
"prevdoc_detail_docname"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -62,6 +64,8 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "prevdoc_detail_docname.sales_person",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "service_person",
|
"fieldname": "service_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -83,49 +87,30 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "prevdoc_doctype",
|
"fieldname": "prevdoc_doctype",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Document Type",
|
"label": "Document Type",
|
||||||
"no_copy": 1,
|
"options": "DocType"
|
||||||
"oldfieldname": "prevdoc_doctype",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"options": "DocType",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_width": "150px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 1,
|
|
||||||
"width": "150px"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "prevdoc_docname",
|
"fieldname": "prevdoc_docname",
|
||||||
"fieldtype": "Dynamic Link",
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 1,
|
||||||
"label": "Against Document No",
|
"label": "Against Document No",
|
||||||
"no_copy": 1,
|
"options": "prevdoc_doctype"
|
||||||
"oldfieldname": "prevdoc_docname",
|
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"options": "prevdoc_doctype",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_width": "160px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 1,
|
|
||||||
"width": "160px"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "prevdoc_detail_docname",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 1,
|
},
|
||||||
"label": "Against Document Detail No",
|
{
|
||||||
"no_copy": 1,
|
"fieldname": "section_break_6",
|
||||||
"oldfieldname": "prevdoc_detail_docname",
|
"fieldtype": "Section Break"
|
||||||
"oldfieldtype": "Data",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_width": "160px",
|
|
||||||
"read_only": 1,
|
|
||||||
"report_hide": 1,
|
|
||||||
"width": "160px"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2020-09-18 17:26:09.703215",
|
"links": [],
|
||||||
|
"modified": "2021-05-27 17:47:21.474282",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Maintenance",
|
"module": "Maintenance",
|
||||||
"name": "Maintenance Visit Purpose",
|
"name": "Maintenance Visit Purpose",
|
||||||
|
@ -433,6 +433,7 @@ def make_material_request(source_name, target_doc=None):
|
|||||||
def make_stock_entry(source_name, target_doc=None):
|
def make_stock_entry(source_name, target_doc=None):
|
||||||
def update_item(obj, target, source_parent):
|
def update_item(obj, target, source_parent):
|
||||||
target.t_warehouse = source_parent.wip_warehouse
|
target.t_warehouse = source_parent.wip_warehouse
|
||||||
|
if not target.conversion_factor:
|
||||||
target.conversion_factor = 1
|
target.conversion_factor = 1
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
|
@ -212,6 +212,8 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_items: function (frm) {
|
get_items: function (frm) {
|
||||||
|
frm.clear_table('prod_plan_references');
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "get_items",
|
method: "get_items",
|
||||||
freeze: true,
|
freeze: true,
|
||||||
@ -221,6 +223,15 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
combine_items: function (frm) {
|
||||||
|
frm.clear_table('prod_plan_references');
|
||||||
|
|
||||||
|
frappe.call({
|
||||||
|
method: "get_items",
|
||||||
|
freeze: true,
|
||||||
|
doc: frm.doc,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
get_items_for_mr: function(frm) {
|
get_items_for_mr: function(frm) {
|
||||||
if (!frm.doc.for_warehouse) {
|
if (!frm.doc.for_warehouse) {
|
||||||
|
@ -28,7 +28,10 @@
|
|||||||
"material_requests",
|
"material_requests",
|
||||||
"select_items_to_manufacture_section",
|
"select_items_to_manufacture_section",
|
||||||
"get_items",
|
"get_items",
|
||||||
|
"combine_items",
|
||||||
"po_items",
|
"po_items",
|
||||||
|
"section_break_25",
|
||||||
|
"prod_plan_references",
|
||||||
"material_request_planning",
|
"material_request_planning",
|
||||||
"include_non_stock_items",
|
"include_non_stock_items",
|
||||||
"include_subcontracted_items",
|
"include_subcontracted_items",
|
||||||
@ -316,13 +319,31 @@
|
|||||||
"fieldname": "include_safety_stock",
|
"fieldname": "include_safety_stock",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Include Safety Stock in Required Qty Calculation"
|
"label": "Include Safety Stock in Required Qty Calculation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.get_items_from == 'Sales Order'",
|
||||||
|
"fieldname": "combine_items",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Consolidate Items"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_25",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "prod_plan_references",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "Production Plan Item Reference",
|
||||||
|
"options": "Production Plan Item Reference"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-03-08 11:17:25.470147",
|
"modified": "2021-05-24 16:59:03.643211",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
@ -96,8 +96,10 @@ class ProductionPlan(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_items(self):
|
def get_items(self):
|
||||||
|
self.set('po_items', [])
|
||||||
if self.get_items_from == "Sales Order":
|
if self.get_items_from == "Sales Order":
|
||||||
self.get_so_items()
|
self.get_so_items()
|
||||||
|
|
||||||
elif self.get_items_from == "Material Request":
|
elif self.get_items_from == "Material Request":
|
||||||
self.get_mr_items()
|
self.get_mr_items()
|
||||||
|
|
||||||
@ -165,9 +167,31 @@ class ProductionPlan(Document):
|
|||||||
self.calculate_total_planned_qty()
|
self.calculate_total_planned_qty()
|
||||||
|
|
||||||
def add_items(self, items):
|
def add_items(self, items):
|
||||||
self.set('po_items', [])
|
refs = {}
|
||||||
for data in items:
|
for data in items:
|
||||||
item_details = get_item_details(data.item_code)
|
item_details = get_item_details(data.item_code)
|
||||||
|
if self.combine_items:
|
||||||
|
if item_details.bom_no in refs:
|
||||||
|
refs[item_details.bom_no]['so_details'].append({
|
||||||
|
'sales_order': data.parent,
|
||||||
|
'sales_order_item': data.name,
|
||||||
|
'qty': data.pending_qty
|
||||||
|
})
|
||||||
|
refs[item_details.bom_no]['qty'] += data.pending_qty
|
||||||
|
continue
|
||||||
|
|
||||||
|
else:
|
||||||
|
refs[item_details.bom_no] = {
|
||||||
|
'qty': data.pending_qty,
|
||||||
|
'po_item_ref': data.name,
|
||||||
|
'so_details': []
|
||||||
|
}
|
||||||
|
refs[item_details.bom_no]['so_details'].append({
|
||||||
|
'sales_order': data.parent,
|
||||||
|
'sales_order_item': data.name,
|
||||||
|
'qty': data.pending_qty
|
||||||
|
})
|
||||||
|
|
||||||
pi = self.append('po_items', {
|
pi = self.append('po_items', {
|
||||||
'include_exploded_items': 1,
|
'include_exploded_items': 1,
|
||||||
'warehouse': data.warehouse,
|
'warehouse': data.warehouse,
|
||||||
@ -191,6 +215,23 @@ class ProductionPlan(Document):
|
|||||||
pi.material_request_item = data.name
|
pi.material_request_item = data.name
|
||||||
pi.description = data.description
|
pi.description = data.description
|
||||||
|
|
||||||
|
if refs:
|
||||||
|
for po_item in self.po_items:
|
||||||
|
po_item.planned_qty = refs[po_item.bom_no]['qty']
|
||||||
|
po_item.pending_qty = refs[po_item.bom_no]['qty']
|
||||||
|
po_item.sales_order = ''
|
||||||
|
self.add_pp_ref(refs)
|
||||||
|
|
||||||
|
def add_pp_ref(self, refs):
|
||||||
|
for bom_no in refs:
|
||||||
|
for so_detail in refs[bom_no]['so_details']:
|
||||||
|
self.append('prod_plan_references', {
|
||||||
|
'item_reference': refs[bom_no]['po_item_ref'],
|
||||||
|
'sales_order': so_detail['sales_order'],
|
||||||
|
'sales_order_item': so_detail['sales_order_item'],
|
||||||
|
'qty': so_detail['qty']
|
||||||
|
})
|
||||||
|
|
||||||
def calculate_total_planned_qty(self):
|
def calculate_total_planned_qty(self):
|
||||||
self.total_planned_qty = 0
|
self.total_planned_qty = 0
|
||||||
for d in self.po_items:
|
for d in self.po_items:
|
||||||
|
@ -100,7 +100,7 @@ class TestProductionPlan(unittest.TestCase):
|
|||||||
|
|
||||||
def test_production_plan_sales_orders(self):
|
def test_production_plan_sales_orders(self):
|
||||||
item = 'Test Production Item 1'
|
item = 'Test Production Item 1'
|
||||||
so = make_sales_order(item_code=item, qty=5)
|
so = make_sales_order(item_code=item, qty=1)
|
||||||
sales_order = so.name
|
sales_order = so.name
|
||||||
sales_order_item = so.items[0].name
|
sales_order_item = so.items[0].name
|
||||||
|
|
||||||
@ -124,8 +124,8 @@ class TestProductionPlan(unittest.TestCase):
|
|||||||
|
|
||||||
wo_doc = frappe.get_doc('Work Order', work_order)
|
wo_doc = frappe.get_doc('Work Order', work_order)
|
||||||
wo_doc.update({
|
wo_doc.update({
|
||||||
'wip_warehouse': '_Test Warehouse 1 - _TC',
|
'wip_warehouse': 'Work In Progress - _TC',
|
||||||
'fg_warehouse': '_Test Warehouse - _TC'
|
'fg_warehouse': 'Finished Goods - _TC'
|
||||||
})
|
})
|
||||||
wo_doc.submit()
|
wo_doc.submit()
|
||||||
|
|
||||||
@ -145,6 +145,58 @@ class TestProductionPlan(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(sales_orders, [])
|
self.assertEqual(sales_orders, [])
|
||||||
|
|
||||||
|
def test_production_plan_combine_items(self):
|
||||||
|
item = 'Test Production Item 1'
|
||||||
|
so = make_sales_order(item_code=item, qty=1)
|
||||||
|
|
||||||
|
pln = frappe.new_doc('Production Plan')
|
||||||
|
pln.company = so.company
|
||||||
|
pln.get_items_from = 'Sales Order'
|
||||||
|
pln.append('sales_orders', {
|
||||||
|
'sales_order': so.name,
|
||||||
|
'sales_order_date': so.transaction_date,
|
||||||
|
'customer': so.customer,
|
||||||
|
'grand_total': so.grand_total
|
||||||
|
})
|
||||||
|
so = make_sales_order(item_code=item, qty=2)
|
||||||
|
pln.append('sales_orders', {
|
||||||
|
'sales_order': so.name,
|
||||||
|
'sales_order_date': so.transaction_date,
|
||||||
|
'customer': so.customer,
|
||||||
|
'grand_total': so.grand_total
|
||||||
|
})
|
||||||
|
pln.combine_items = 1
|
||||||
|
pln.get_items()
|
||||||
|
pln.submit()
|
||||||
|
|
||||||
|
self.assertTrue(pln.po_items[0].planned_qty, 3)
|
||||||
|
|
||||||
|
pln.make_work_order()
|
||||||
|
work_order = frappe.db.get_value('Work Order', {
|
||||||
|
'production_plan_item': pln.po_items[0].name,
|
||||||
|
'production_plan': pln.name
|
||||||
|
}, 'name')
|
||||||
|
|
||||||
|
wo_doc = frappe.get_doc('Work Order', work_order)
|
||||||
|
wo_doc.update({
|
||||||
|
'wip_warehouse': 'Work In Progress - _TC',
|
||||||
|
})
|
||||||
|
|
||||||
|
wo_doc.submit()
|
||||||
|
so_items = []
|
||||||
|
for plan_reference in pln.prod_plan_references:
|
||||||
|
so_items.append(plan_reference.sales_order_item)
|
||||||
|
so_wo_qty = frappe.db.get_value('Sales Order Item', plan_reference.sales_order_item, 'work_order_qty')
|
||||||
|
self.assertEqual(so_wo_qty, plan_reference.qty)
|
||||||
|
|
||||||
|
wo_doc.cancel()
|
||||||
|
for so_item in so_items:
|
||||||
|
so_wo_qty = frappe.db.get_value('Sales Order Item', so_item, 'work_order_qty')
|
||||||
|
self.assertEqual(so_wo_qty, 0.0)
|
||||||
|
|
||||||
|
latest_plan = frappe.get_doc('Production Plan', pln.name)
|
||||||
|
latest_plan.cancel()
|
||||||
|
|
||||||
def test_pp_to_mr_customer_provided(self):
|
def test_pp_to_mr_customer_provided(self):
|
||||||
#Material Request from Production Plan for Customer Provided
|
#Material Request from Production Plan for Customer Provided
|
||||||
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
|
create_item('CUST-0987', is_customer_provided_item = 1, customer = '_Test Customer', is_purchase_item = 0)
|
||||||
|
@ -1,792 +1,229 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "hash",
|
"autoname": "hash",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2013-02-22 01:27:49",
|
"creation": "2013-02-22 01:27:49",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"include_exploded_items",
|
||||||
|
"item_code",
|
||||||
|
"bom_no",
|
||||||
|
"planned_qty",
|
||||||
|
"column_break_6",
|
||||||
|
"make_work_order_for_sub_assembly_items",
|
||||||
|
"warehouse",
|
||||||
|
"planned_start_date",
|
||||||
|
"section_break_9",
|
||||||
|
"pending_qty",
|
||||||
|
"ordered_qty",
|
||||||
|
"produced_qty",
|
||||||
|
"column_break_17",
|
||||||
|
"description",
|
||||||
|
"stock_uom",
|
||||||
|
"reference_section",
|
||||||
|
"sales_order",
|
||||||
|
"sales_order_item",
|
||||||
|
"column_break_19",
|
||||||
|
"material_request",
|
||||||
|
"material_request_item",
|
||||||
|
"product_bundle_item",
|
||||||
|
"item_reference"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
"default": "0",
|
||||||
"fieldname": "include_exploded_items",
|
"fieldname": "include_exploded_items",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
"label": "Include Exploded Items"
|
||||||
"label": "Include Exploded Items",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "item_code",
|
"fieldname": "item_code",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Item Code",
|
"label": "Item Code",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "item_code",
|
"oldfieldname": "item_code",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "bom_no",
|
"fieldname": "bom_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "BOM No",
|
"label": "BOM No",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "bom_no",
|
"oldfieldname": "bom_no",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "BOM",
|
"options": "BOM",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "planned_qty",
|
"fieldname": "planned_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Planned Qty",
|
"label": "Planned Qty",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "planned_qty",
|
"oldfieldname": "planned_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_6",
|
"fieldname": "column_break_6",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"depends_on": "",
|
|
||||||
"description": "If enabled, system will create the work order for the exploded items against which BOM is available.",
|
"description": "If enabled, system will create the work order for the exploded items against which BOM is available.",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "make_work_order_for_sub_assembly_items",
|
"fieldname": "make_work_order_for_sub_assembly_items",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Make Work Order for Sub Assembly Items"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Make Work Order for Sub Assembly Items",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"description": "",
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "warehouse",
|
"fieldname": "warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "For Warehouse",
|
"label": "For Warehouse",
|
||||||
"length": 0,
|
"options": "Warehouse"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Warehouse",
|
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "Today",
|
"default": "Today",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "planned_start_date",
|
"fieldname": "planned_start_date",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Planned Start Date",
|
"label": "Planned Start Date",
|
||||||
"length": 0,
|
"reqd": 1
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "section_break_9",
|
"fieldname": "section_break_9",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Quantity and Description"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Quantity and Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "pending_qty",
|
"fieldname": "pending_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Pending Qty",
|
"label": "Pending Qty",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "prevdoc_reqd_qty",
|
"oldfieldname": "prevdoc_reqd_qty",
|
||||||
"oldfieldtype": "Currency",
|
"oldfieldtype": "Currency",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "100px",
|
"print_width": "100px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "100px"
|
"width": "100px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "ordered_qty",
|
"fieldname": "ordered_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Ordered Qty",
|
"label": "Ordered Qty",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "produced_qty",
|
"fieldname": "produced_qty",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Produced Qty",
|
"label": "Produced Qty",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_17",
|
"fieldname": "column_break_17",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "description",
|
"oldfieldname": "description",
|
||||||
"oldfieldtype": "Text",
|
"oldfieldtype": "Text",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "200px",
|
"print_width": "200px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "200px"
|
"width": "200px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "stock_uom",
|
"fieldname": "stock_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "stock_uom",
|
"oldfieldname": "stock_uom",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "UOM",
|
"options": "UOM",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"print_width": "80px",
|
"print_width": "80px",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0,
|
|
||||||
"width": "80px"
|
"width": "80px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "reference_section",
|
"fieldname": "reference_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Reference"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Reference",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "sales_order",
|
"fieldname": "sales_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sales Order",
|
"label": "Sales Order",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"oldfieldname": "source_docname",
|
"oldfieldname": "source_docname",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
"options": "Sales Order",
|
"options": "Sales Order",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "sales_order_item",
|
"fieldname": "sales_order_item",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Sales Order Item",
|
"label": "Sales Order Item",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"print_hide": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "column_break_19",
|
"fieldname": "column_break_19",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break"
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "material_request",
|
"fieldname": "material_request",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Material Request",
|
"label": "Material Request",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Material Request",
|
"options": "Material Request",
|
||||||
"permlevel": 0,
|
"read_only": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "material_request_item",
|
"fieldname": "material_request_item",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"ignore_user_permissions": 0,
|
"label": "material_request_item"
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "material_request_item",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 0,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fetch_if_empty": 0,
|
|
||||||
"fieldname": "product_bundle_item",
|
"fieldname": "product_bundle_item",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Product Bundle Item",
|
"label": "Product Bundle Item",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Item",
|
"options": "Item",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
},
|
||||||
"remember_last_selected_value": 0,
|
{
|
||||||
"report_hide": 0,
|
"fieldname": "item_reference",
|
||||||
"reqd": 0,
|
"fieldtype": "Data",
|
||||||
"search_index": 0,
|
"hidden": 1,
|
||||||
"set_only_once": 0,
|
"label": "Item Reference"
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2019-04-08 23:09:57.199423",
|
"modified": "2021-04-28 19:14:57.772123",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan Item",
|
"name": "Production Plan Item",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
"sort_field": "modified",
|
||||||
"read_only": 0,
|
"sort_order": "ASC"
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_order": "ASC",
|
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user