Merge branch 'develop' of https://github.com/frappe/erpnext into loan_fixes_phase_2
This commit is contained in:
commit
a42194f3d5
32
.flake8
Normal file
32
.flake8
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[flake8]
|
||||||
|
ignore =
|
||||||
|
E121,
|
||||||
|
E126,
|
||||||
|
E127,
|
||||||
|
E128,
|
||||||
|
E203,
|
||||||
|
E225,
|
||||||
|
E226,
|
||||||
|
E231,
|
||||||
|
E241,
|
||||||
|
E251,
|
||||||
|
E261,
|
||||||
|
E265,
|
||||||
|
E302,
|
||||||
|
E303,
|
||||||
|
E305,
|
||||||
|
E402,
|
||||||
|
E501,
|
||||||
|
E741,
|
||||||
|
W291,
|
||||||
|
W292,
|
||||||
|
W293,
|
||||||
|
W391,
|
||||||
|
W503,
|
||||||
|
W504,
|
||||||
|
F403,
|
||||||
|
B007,
|
||||||
|
B950,
|
||||||
|
W191,
|
||||||
|
|
||||||
|
max-line-length = 200
|
10
.github/helper/install.sh
vendored
10
.github/helper/install.sh
vendored
@ -1,14 +1,18 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
cd ~ || exit
|
cd ~ || exit
|
||||||
|
|
||||||
sudo apt-get install redis-server
|
sudo apt-get install redis-server
|
||||||
|
|
||||||
nvm install 10
|
sudo apt install nodejs
|
||||||
|
|
||||||
|
sudo apt install npm
|
||||||
|
|
||||||
pip install frappe-bench
|
pip install frappe-bench
|
||||||
|
|
||||||
git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF}" --depth 1
|
git clone https://github.com/frappe/frappe --branch "${GITHUB_BASE_REF:-${GITHUB_REF##*/}}" --depth 1
|
||||||
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
|
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
|
||||||
|
|
||||||
mkdir ~/frappe-bench/sites/test_site
|
mkdir ~/frappe-bench/sites/test_site
|
||||||
@ -39,4 +43,4 @@ sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
|
|||||||
|
|
||||||
bench get-app erpnext "${GITHUB_WORKSPACE}"
|
bench get-app erpnext "${GITHUB_WORKSPACE}"
|
||||||
bench start &
|
bench start &
|
||||||
bench --site test_site reinstall --yes
|
bench --site test_site reinstall --yes
|
||||||
|
14
.github/helper/run_tests.sh
vendored
14
.github/helper/run_tests.sh
vendored
@ -1,14 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cd ~/frappe-bench/ || exit
|
|
||||||
|
|
||||||
|
|
||||||
if [ "$TYPE" == "server" ]; then
|
|
||||||
bench --site test_site run-tests --app erpnext --coverage
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TYPE" == "patch" ]; then
|
|
||||||
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
|
|
||||||
fi
|
|
14
.github/workflows/ci-tests.yml
vendored
14
.github/workflows/ci-tests.yml
vendored
@ -1,21 +1,22 @@
|
|||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on: [pull_request, workflow_dispatch, push]
|
||||||
pull_request:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-18.04
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- TYPE: "server"
|
- TYPE: "server"
|
||||||
JOB_NAME: "Server"
|
JOB_NAME: "Server"
|
||||||
|
RUN_COMMAND: cd ~/frappe-bench/ && bench --site test_site run-tests --app erpnext --coverage
|
||||||
- TYPE: "patch"
|
- TYPE: "patch"
|
||||||
JOB_NAME: "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: ${{ matrix.JOB_NAME }}
|
||||||
|
|
||||||
@ -48,7 +49,6 @@ 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,7 +60,6 @@ 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)"
|
||||||
@ -77,7 +76,7 @@ jobs:
|
|||||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
|
||||||
|
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: bash ${GITHUB_WORKSPACE}/.github/helper/run_tests.sh
|
run: ${{ matrix.RUN_COMMAND }}
|
||||||
env:
|
env:
|
||||||
TYPE: ${{ matrix.TYPE }}
|
TYPE: ${{ matrix.TYPE }}
|
||||||
|
|
||||||
@ -92,3 +91,4 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<p>ERP made simple</p>
|
<p>ERP made simple</p>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://travis-ci.com/frappe/erpnext)
|
[](https://github.com/frappe/erpnext/actions/workflows/ci-tests.yml)
|
||||||
[](https://www.codetriage.com/frappe/erpnext)
|
[](https://www.codetriage.com/frappe/erpnext)
|
||||||
[](https://coveralls.io/github/frappe/erpnext?branch=develop)
|
[](https://coveralls.io/github/frappe/erpnext?branch=develop)
|
||||||
|
|
||||||
|
@ -214,6 +214,7 @@ class Account(NestedSet):
|
|||||||
if parent_value_changed:
|
if parent_value_changed:
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
throw(_("Account with child nodes cannot be converted to ledger"))
|
throw(_("Account with child nodes cannot be converted to ledger"))
|
||||||
@ -224,6 +225,7 @@ class Account(NestedSet):
|
|||||||
self.save()
|
self.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_ledger_to_group(self):
|
def convert_ledger_to_group(self):
|
||||||
if self.check_gle_exists():
|
if self.check_gle_exists():
|
||||||
throw(_("Account with existing transaction can not be converted to group."))
|
throw(_("Account with existing transaction can not be converted to group."))
|
||||||
|
@ -39,6 +39,7 @@ class AccountingPeriod(Document):
|
|||||||
frappe.throw(_("Accounting Period overlaps with {0}")
|
frappe.throw(_("Accounting Period overlaps with {0}")
|
||||||
.format(existing_accounting_period[0].get("name")), OverlapError)
|
.format(existing_accounting_period[0].get("name")), OverlapError)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_doctypes_for_closing(self):
|
def get_doctypes_for_closing(self):
|
||||||
docs_for_closing = []
|
docs_for_closing = []
|
||||||
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
|
||||||
|
@ -11,36 +11,36 @@ from erpnext.accounts.doctype.accounting_period.accounting_period import Overlap
|
|||||||
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
|
||||||
|
|
||||||
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",
|
||||||
end_date = "2018-06-30", company = "Wind Power LLC")
|
end_date = "2018-06-30", company = "Wind Power LLC")
|
||||||
ap1.save()
|
ap1.save()
|
||||||
|
|
||||||
ap2 = create_accounting_period(start_date = "2018-06-30",
|
ap2 = create_accounting_period(start_date = "2018-06-30",
|
||||||
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
|
||||||
self.assertRaises(OverlapError, ap2.save)
|
self.assertRaises(OverlapError, ap2.save)
|
||||||
|
|
||||||
def test_accounting_period(self):
|
def test_accounting_period(self):
|
||||||
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
|
||||||
ap1.save()
|
ap1.save()
|
||||||
|
|
||||||
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
|
doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
|
||||||
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
self.assertRaises(ClosedAccountingPeriod, doc.submit)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
for d in frappe.get_all("Accounting Period"):
|
for d in frappe.get_all("Accounting Period"):
|
||||||
frappe.delete_doc("Accounting Period", d.name)
|
frappe.delete_doc("Accounting Period", d.name)
|
||||||
|
|
||||||
def create_accounting_period(**args):
|
def create_accounting_period(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
|
||||||
accounting_period = frappe.new_doc("Accounting Period")
|
accounting_period = frappe.new_doc("Accounting Period")
|
||||||
accounting_period.start_date = args.start_date or nowdate()
|
accounting_period.start_date = args.start_date or nowdate()
|
||||||
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
|
||||||
accounting_period.company = args.company or "_Test Company"
|
accounting_period.company = args.company or "_Test Company"
|
||||||
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
|
||||||
accounting_period.append("closed_documents", {
|
accounting_period.append("closed_documents", {
|
||||||
"document_type": 'Sales Invoice', "closed": 1
|
"document_type": 'Sales Invoice', "closed": 1
|
||||||
})
|
})
|
||||||
|
|
||||||
return accounting_period
|
return accounting_period
|
||||||
|
@ -12,6 +12,7 @@ form_grid_templates = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class BankClearance(Document):
|
class BankClearance(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_payment_entries(self):
|
def get_payment_entries(self):
|
||||||
if not (self.from_date and self.to_date):
|
if not (self.from_date and self.to_date):
|
||||||
frappe.throw(_("From Date and To Date are Mandatory"))
|
frappe.throw(_("From Date and To Date are Mandatory"))
|
||||||
@ -108,6 +109,7 @@ class BankClearance(Document):
|
|||||||
row.update(d)
|
row.update(d)
|
||||||
self.total_amount += flt(amount)
|
self.total_amount += flt(amount)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_clearance_date(self):
|
def update_clearance_date(self):
|
||||||
clearance_date_updated = False
|
clearance_date_updated = False
|
||||||
for d in self.get('payment_entries'):
|
for d in self.get('payment_entries'):
|
||||||
|
@ -8,6 +8,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
|||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
company: ["in", frm.doc.company],
|
company: ["in", frm.doc.company],
|
||||||
|
'is_company_account': 1
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -532,43 +532,4 @@ frappe.ui.form.on("Bank Statement Import", {
|
|||||||
</table>
|
</table>
|
||||||
`);
|
`);
|
||||||
},
|
},
|
||||||
|
|
||||||
show_missing_link_values(frm, missing_link_values) {
|
|
||||||
let can_be_created_automatically = missing_link_values.every(
|
|
||||||
(d) => d.has_one_mandatory_field
|
|
||||||
);
|
|
||||||
|
|
||||||
let html = missing_link_values
|
|
||||||
.map((d) => {
|
|
||||||
let doctype = d.doctype;
|
|
||||||
let values = d.missing_values;
|
|
||||||
return `
|
|
||||||
<h5>${doctype}</h5>
|
|
||||||
<ul>${values.map((v) => `<li>${v}</li>`).join("")}</ul>
|
|
||||||
`;
|
|
||||||
})
|
|
||||||
.join("");
|
|
||||||
|
|
||||||
if (can_be_created_automatically) {
|
|
||||||
// prettier-ignore
|
|
||||||
let message = __('There are some linked records which needs to be created before we can import your file. Do you want to create the following missing records automatically?');
|
|
||||||
frappe.confirm(message + html, () => {
|
|
||||||
frm.call("create_missing_link_values", {
|
|
||||||
missing_link_values,
|
|
||||||
}).then((r) => {
|
|
||||||
let records = r.message;
|
|
||||||
frappe.msgprint(__(
|
|
||||||
"Created {0} records successfully.", [
|
|
||||||
records.length,
|
|
||||||
]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
frappe.msgprint(
|
|
||||||
// prettier-ignore
|
|
||||||
__('The following records needs to be created before we can import your file.') + html
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
@ -15,12 +15,14 @@ from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profi
|
|||||||
test_dependencies = ["Item", "Cost Center"]
|
test_dependencies = ["Item", "Cost Center"]
|
||||||
|
|
||||||
class TestBankTransaction(unittest.TestCase):
|
class TestBankTransaction(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
make_pos_profile()
|
make_pos_profile()
|
||||||
add_transactions()
|
add_transactions()
|
||||||
add_vouchers()
|
add_vouchers()
|
||||||
|
|
||||||
def tearDown(self):
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
for bt in frappe.get_all("Bank Transaction"):
|
for bt in frappe.get_all("Bank Transaction"):
|
||||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
@ -33,9 +35,6 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
# Delete POS Profile
|
# Delete POS Profile
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
frappe.flags.test_bank_transactions_created = False
|
|
||||||
frappe.flags.test_payments_created = False
|
|
||||||
|
|
||||||
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
|
||||||
def test_linked_payments(self):
|
def test_linked_payments(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
|
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
|
||||||
@ -44,8 +43,8 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
|
|
||||||
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
|
||||||
def test_reconcile(self):
|
def test_reconcile(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
|
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
|
||||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
|
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
|
||||||
vouchers = json.dumps([{
|
vouchers = json.dumps([{
|
||||||
"payment_doctype":"Payment Entry",
|
"payment_doctype":"Payment Entry",
|
||||||
"payment_name":payment.name,
|
"payment_name":payment.name,
|
||||||
@ -62,7 +61,6 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
def test_debit_credit_output(self):
|
def test_debit_credit_output(self):
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
||||||
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
|
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
|
||||||
print(linked_payments)
|
|
||||||
self.assertTrue(linked_payments[0][3])
|
self.assertTrue(linked_payments[0][3])
|
||||||
|
|
||||||
# Check error if already reconciled
|
# Check error if already reconciled
|
||||||
@ -116,10 +114,6 @@ def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def add_transactions():
|
def add_transactions():
|
||||||
if frappe.flags.test_bank_transactions_created:
|
|
||||||
return
|
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
create_bank_account()
|
create_bank_account()
|
||||||
|
|
||||||
doc = frappe.get_doc({
|
doc = frappe.get_doc({
|
||||||
@ -172,14 +166,8 @@ def add_transactions():
|
|||||||
}).insert()
|
}).insert()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
|
|
||||||
frappe.flags.test_bank_transactions_created = True
|
|
||||||
|
|
||||||
def add_vouchers():
|
def add_vouchers():
|
||||||
if frappe.flags.test_payments_created:
|
|
||||||
return
|
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
@ -272,13 +260,6 @@ def add_vouchers():
|
|||||||
except frappe.DuplicateEntryError:
|
except frappe.DuplicateEntryError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080)
|
|
||||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
|
|
||||||
pe.reference_no = "Fayva Oct 18"
|
|
||||||
pe.reference_date = "2018-10-29"
|
|
||||||
pe.insert()
|
|
||||||
pe.submit()
|
|
||||||
|
|
||||||
mode_of_payment = frappe.get_doc({
|
mode_of_payment = frappe.get_doc({
|
||||||
"doctype": "Mode of Payment",
|
"doctype": "Mode of Payment",
|
||||||
"name": "Cash"
|
"name": "Cash"
|
||||||
@ -291,14 +272,12 @@ def add_vouchers():
|
|||||||
})
|
})
|
||||||
mode_of_payment.save()
|
mode_of_payment.save()
|
||||||
|
|
||||||
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_submit=1)
|
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
|
||||||
si.is_pos = 1
|
si.is_pos = 1
|
||||||
si.append("payments", {
|
si.append("payments", {
|
||||||
"mode_of_payment": "Cash",
|
"mode_of_payment": "Cash",
|
||||||
"account": "_Test Bank - _TC",
|
"account": "_Test Bank - _TC",
|
||||||
"amount": 109080
|
"amount": 109080
|
||||||
})
|
})
|
||||||
si.save()
|
si.insert()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
|
||||||
frappe.flags.test_payments_created = True
|
|
||||||
|
@ -57,6 +57,7 @@ class CForm(Document):
|
|||||||
total = sum([flt(d.grand_total) for d in self.get('invoices')])
|
total = sum([flt(d.grand_total) for d in self.get('invoices')])
|
||||||
frappe.db.set(self, 'total_invoiced_amount', total)
|
frappe.db.set(self, 'total_invoiced_amount', total)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_invoice_details(self, invoice_no):
|
def get_invoice_details(self, invoice_no):
|
||||||
""" Pull details from invoices for referrence """
|
""" Pull details from invoices for referrence """
|
||||||
if invoice_no:
|
if invoice_no:
|
||||||
|
@ -50,6 +50,7 @@ class CostCenter(NestedSet):
|
|||||||
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
|
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
|
||||||
frappe.bold(self.parent_cost_center)))
|
frappe.bold(self.parent_cost_center)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_group_to_ledger(self):
|
def convert_group_to_ledger(self):
|
||||||
if self.check_if_child_exists():
|
if self.check_if_child_exists():
|
||||||
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
|
||||||
@ -60,6 +61,7 @@ class CostCenter(NestedSet):
|
|||||||
self.save()
|
self.save()
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def convert_ledger_to_group(self):
|
def convert_ledger_to_group(self):
|
||||||
if cint(self.enable_distributed_cost_center):
|
if cint(self.enable_distributed_cost_center):
|
||||||
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
|
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
|
||||||
|
@ -27,6 +27,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
if not (self.company and self.posting_date):
|
if not (self.company and self.posting_date):
|
||||||
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
frappe.throw(_("Please select Company and Posting Date to getting entries"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_accounts_data(self, account=None):
|
def get_accounts_data(self, account=None):
|
||||||
accounts = []
|
accounts = []
|
||||||
self.validate_mandatory()
|
self.validate_mandatory()
|
||||||
@ -95,6 +96,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
message = _("No outstanding invoices found")
|
message = _("No outstanding invoices found")
|
||||||
frappe.msgprint(message)
|
frappe.msgprint(message)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_jv_entry(self):
|
def make_jv_entry(self):
|
||||||
if self.total_gain_loss == 0:
|
if self.total_gain_loss == 0:
|
||||||
return
|
return
|
||||||
|
@ -12,6 +12,7 @@ from frappe.model.document import Document
|
|||||||
class FiscalYearIncorrectDate(frappe.ValidationError): pass
|
class FiscalYearIncorrectDate(frappe.ValidationError): pass
|
||||||
|
|
||||||
class FiscalYear(Document):
|
class FiscalYear(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def set_as_default(self):
|
def set_as_default(self):
|
||||||
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
|
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
|
||||||
global_defaults = frappe.get_doc("Global Defaults")
|
global_defaults = frappe.get_doc("Global Defaults")
|
||||||
@ -54,7 +55,7 @@ class FiscalYear(Document):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
check_duplicate_fiscal_year(self)
|
check_duplicate_fiscal_year(self)
|
||||||
frappe.cache().delete_value("fiscal_years")
|
frappe.cache().delete_value("fiscal_years")
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
global_defaults = frappe.get_doc("Global Defaults")
|
global_defaults = frappe.get_doc("Global Defaults")
|
||||||
if global_defaults.current_fiscal_year == self.name:
|
if global_defaults.current_fiscal_year == self.name:
|
||||||
|
@ -290,4 +290,8 @@ def rename_temporarily_named_docs(doctype):
|
|||||||
oldname = doc.name
|
oldname = doc.name
|
||||||
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
|
||||||
newname = doc.name
|
newname = doc.name
|
||||||
frappe.db.sql("""UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s""".format(doctype), (newname, oldname))
|
frappe.db.sql(
|
||||||
|
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
|
||||||
|
(newname, oldname),
|
||||||
|
auto_commit=True
|
||||||
|
)
|
||||||
|
@ -125,6 +125,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
|
|
||||||
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_disbursement_entry(self):
|
def create_disbursement_entry(self):
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
je.voucher_type = 'Journal Entry'
|
je.voucher_type = 'Journal Entry'
|
||||||
@ -174,6 +175,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
|
|
||||||
return je
|
return je
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def close_loan(self):
|
def close_loan(self):
|
||||||
je = frappe.new_doc("Journal Entry")
|
je = frappe.new_doc("Journal Entry")
|
||||||
je.voucher_type = 'Journal Entry'
|
je.voucher_type = 'Journal Entry'
|
||||||
|
@ -564,6 +564,7 @@ class JournalEntry(AccountsController):
|
|||||||
if gl_map:
|
if gl_map:
|
||||||
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_balance(self):
|
def get_balance(self):
|
||||||
if not self.get('accounts'):
|
if not self.get('accounts'):
|
||||||
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
||||||
|
@ -8,6 +8,7 @@ from frappe.utils import (flt, add_months)
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class MonthlyDistribution(Document):
|
class MonthlyDistribution(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_months(self):
|
def get_months(self):
|
||||||
month_list = ['January','February','March','April','May','June','July','August','September',
|
month_list = ['January','February','March','April','May','June','July','August','September',
|
||||||
'October','November','December']
|
'October','November','December']
|
||||||
|
@ -167,6 +167,7 @@ class OpeningInvoiceCreationTool(Document):
|
|||||||
|
|
||||||
return invoice
|
return invoice
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_invoices(self):
|
def make_invoices(self):
|
||||||
self.validate_company()
|
self.validate_company()
|
||||||
invoices = self.get_invoices()
|
invoices = self.get_invoices()
|
||||||
|
@ -6,10 +6,12 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
test_dependencies = ["Customer", "Supplier"]
|
from frappe.cache_manager import clear_doctype_cache
|
||||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||||
|
|
||||||
|
test_dependencies = ["Customer", "Supplier"]
|
||||||
|
|
||||||
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
|
||||||
@ -24,22 +26,25 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
|||||||
|
|
||||||
def test_opening_sales_invoice_creation(self):
|
def test_opening_sales_invoice_creation(self):
|
||||||
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
|
||||||
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
try:
|
||||||
|
invoices = self.make_invoices(company="_Test Opening Invoice Company")
|
||||||
|
|
||||||
self.assertEqual(len(invoices), 2)
|
self.assertEqual(len(invoices), 2)
|
||||||
expected_value = {
|
expected_value = {
|
||||||
"keys": ["customer", "outstanding_amount", "status"],
|
"keys": ["customer", "outstanding_amount", "status"],
|
||||||
0: ["_Test Customer", 300, "Overdue"],
|
0: ["_Test Customer", 300, "Overdue"],
|
||||||
1: ["_Test Customer 1", 250, "Overdue"],
|
1: ["_Test Customer 1", 250, "Overdue"],
|
||||||
}
|
}
|
||||||
self.check_expected_values(invoices, expected_value)
|
self.check_expected_values(invoices, expected_value)
|
||||||
|
|
||||||
si = frappe.get_doc("Sales Invoice", invoices[0])
|
si = frappe.get_doc("Sales Invoice", invoices[0])
|
||||||
|
|
||||||
# Check if update stock is not enabled
|
# Check if update stock is not enabled
|
||||||
self.assertEqual(si.update_stock, 0)
|
self.assertEqual(si.update_stock, 0)
|
||||||
|
|
||||||
property_setter.delete()
|
finally:
|
||||||
|
property_setter.delete()
|
||||||
|
clear_doctype_cache("Sales Invoice")
|
||||||
|
|
||||||
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||||
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||||
@ -143,4 +148,4 @@ def make_customer(customer=None):
|
|||||||
customer.insert(ignore_permissions=True)
|
customer.insert(ignore_permissions=True)
|
||||||
return customer.name
|
return customer.name
|
||||||
else:
|
else:
|
||||||
return frappe.db.exists("Customer", customer_name)
|
return frappe.db.exists("Customer", customer_name)
|
||||||
|
@ -605,12 +605,22 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
{fieldtype:"Column Break"},
|
{fieldtype:"Column Break"},
|
||||||
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
|
||||||
{fieldtype:"Section Break"},
|
{fieldtype:"Section Break"},
|
||||||
|
{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
|
||||||
|
"get_query": function() {
|
||||||
|
return {
|
||||||
|
"filters": {"company": frm.doc.company}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{fieldtype:"Column Break"},
|
||||||
|
{fieldtype:"Section Break"},
|
||||||
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
|
||||||
];
|
];
|
||||||
|
|
||||||
frappe.prompt(fields, function(filters){
|
frappe.prompt(fields, function(filters){
|
||||||
frappe.flags.allocate_payment_amount = true;
|
frappe.flags.allocate_payment_amount = true;
|
||||||
frm.events.validate_filters_data(frm, filters);
|
frm.events.validate_filters_data(frm, filters);
|
||||||
|
frm.doc.cost_center = filters.cost_center;
|
||||||
frm.events.get_outstanding_documents(frm, filters);
|
frm.events.get_outstanding_documents(frm, filters);
|
||||||
}, __("Filters"), __("Get Outstanding Documents"));
|
}, __("Filters"), __("Get Outstanding Documents"));
|
||||||
},
|
},
|
||||||
@ -627,13 +637,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
let to_field = fields[key][1];
|
let to_field = fields[key][1];
|
||||||
|
|
||||||
if (filters[from_field] && !filters[to_field]) {
|
if (filters[from_field] && !filters[to_field]) {
|
||||||
frappe.throw(__("Error: {0} is mandatory field",
|
frappe.throw(
|
||||||
[to_field.replace(/_/g, " ")]
|
__("Error: {0} is mandatory field", [to_field.replace(/_/g, " ")])
|
||||||
));
|
);
|
||||||
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
|
||||||
frappe.throw(__("{0}: {1} must be less than {2}",
|
frappe.throw(
|
||||||
[key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")]
|
__("{0}: {1} must be less than {2}", [key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")])
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -682,6 +692,8 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
c.total_amount = d.invoice_amount;
|
c.total_amount = d.invoice_amount;
|
||||||
c.outstanding_amount = d.outstanding_amount;
|
c.outstanding_amount = d.outstanding_amount;
|
||||||
c.bill_no = d.bill_no;
|
c.bill_no = d.bill_no;
|
||||||
|
c.payment_term = d.payment_term;
|
||||||
|
c.allocated_amount = d.allocated_amount;
|
||||||
|
|
||||||
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim", "Fees"], d.voucher_type)) {
|
||||||
if(flt(d.outstanding_amount) > 0)
|
if(flt(d.outstanding_amount) > 0)
|
||||||
@ -764,12 +776,15 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
|
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
|
||||||
if(paid_amount > total_negative_outstanding) {
|
if(paid_amount > total_negative_outstanding) {
|
||||||
if(total_negative_outstanding == 0) {
|
if(total_negative_outstanding == 0) {
|
||||||
frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice",
|
frappe.msgprint(
|
||||||
[frm.doc.payment_type,
|
__("Cannot {0} {1} {2} without any negative outstanding invoice", [frm.doc.payment_type,
|
||||||
(frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type]));
|
(frm.doc.party_type=="Customer" ? "to" : "from"), frm.doc.party_type])
|
||||||
|
);
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
frappe.msgprint(__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding]));
|
frappe.msgprint(
|
||||||
|
__("Paid Amount cannot be greater than total negative outstanding amount {0}", [total_negative_outstanding])
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -781,10 +796,13 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$.each(frm.doc.references || [], function(i, row) {
|
$.each(frm.doc.references || [], function(i, row) {
|
||||||
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
if (frappe.flags.allocate_payment_amount == 0) {
|
||||||
if(frappe.flags.allocate_payment_amount != 0){
|
//If allocate payment amount checkbox is unchecked, set zero to allocate amount
|
||||||
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
row.allocated_amount = 0;
|
||||||
if(row.outstanding_amount >= allocated_positive_outstanding) {
|
|
||||||
|
} else if (frappe.flags.allocate_payment_amount != 0 && !row.allocated_amount) {
|
||||||
|
if (row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
|
||||||
|
if (row.outstanding_amount >= allocated_positive_outstanding) {
|
||||||
row.allocated_amount = allocated_positive_outstanding;
|
row.allocated_amount = allocated_positive_outstanding;
|
||||||
} else {
|
} else {
|
||||||
row.allocated_amount = row.outstanding_amount;
|
row.allocated_amount = row.outstanding_amount;
|
||||||
@ -792,9 +810,11 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
|
|
||||||
allocated_positive_outstanding -= flt(row.allocated_amount);
|
allocated_positive_outstanding -= flt(row.allocated_amount);
|
||||||
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
|
} else if (row.outstanding_amount < 0 && allocated_negative_outstanding) {
|
||||||
if(Math.abs(row.outstanding_amount) >= allocated_negative_outstanding)
|
if (Math.abs(row.outstanding_amount) >= allocated_negative_outstanding) {
|
||||||
row.allocated_amount = -1*allocated_negative_outstanding;
|
row.allocated_amount = -1*allocated_negative_outstanding;
|
||||||
else row.allocated_amount = row.outstanding_amount;
|
} else {
|
||||||
|
row.allocated_amount = row.outstanding_amount;
|
||||||
|
};
|
||||||
|
|
||||||
allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
|
allocated_negative_outstanding -= Math.abs(flt(row.allocated_amount));
|
||||||
}
|
}
|
||||||
@ -1066,11 +1086,6 @@ frappe.ui.form.on('Payment Entry', {
|
|||||||
frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
|
frm.set_value("paid_from_account_balance", r.message.paid_from_account_balance);
|
||||||
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
|
frm.set_value("paid_to_account_balance", r.message.paid_to_account_balance);
|
||||||
frm.set_value("party_balance", r.message.party_balance);
|
frm.set_value("party_balance", r.message.party_balance);
|
||||||
},
|
|
||||||
() => {
|
|
||||||
if(frm.doc.payment_type != "Internal") {
|
|
||||||
frm.clear_table("references");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -333,33 +333,50 @@ class PaymentEntry(AccountsController):
|
|||||||
invoice_payment_amount_map = {}
|
invoice_payment_amount_map = {}
|
||||||
invoice_paid_amount_map = {}
|
invoice_paid_amount_map = {}
|
||||||
|
|
||||||
for reference in self.get('references'):
|
for ref in self.get('references'):
|
||||||
if reference.payment_term and reference.reference_name:
|
if ref.payment_term and ref.reference_name:
|
||||||
key = (reference.payment_term, reference.reference_name)
|
key = (ref.payment_term, ref.reference_name)
|
||||||
invoice_payment_amount_map.setdefault(key, 0.0)
|
invoice_payment_amount_map.setdefault(key, 0.0)
|
||||||
invoice_payment_amount_map[key] += reference.allocated_amount
|
invoice_payment_amount_map[key] += ref.allocated_amount
|
||||||
|
|
||||||
if not invoice_paid_amount_map.get(key):
|
if not invoice_paid_amount_map.get(key):
|
||||||
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': reference.reference_name},
|
payment_schedule = frappe.get_all(
|
||||||
fields=['paid_amount', 'payment_amount', 'payment_term'])
|
'Payment Schedule',
|
||||||
|
filters={'parent': ref.reference_name},
|
||||||
|
fields=['paid_amount', 'payment_amount', 'payment_term', 'discount', 'outstanding']
|
||||||
|
)
|
||||||
for term in payment_schedule:
|
for term in payment_schedule:
|
||||||
invoice_key = (term.payment_term, reference.reference_name)
|
invoice_key = (term.payment_term, ref.reference_name)
|
||||||
invoice_paid_amount_map.setdefault(invoice_key, {})
|
invoice_paid_amount_map.setdefault(invoice_key, {})
|
||||||
invoice_paid_amount_map[invoice_key]['outstanding'] = term.payment_amount - term.paid_amount
|
invoice_paid_amount_map[invoice_key]['outstanding'] = term.outstanding
|
||||||
|
invoice_paid_amount_map[invoice_key]['discounted_amt'] = ref.total_amount * (term.discount / 100)
|
||||||
|
|
||||||
|
for key, allocated_amount in iteritems(invoice_payment_amount_map):
|
||||||
|
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
||||||
|
discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get('discounted_amt'))
|
||||||
|
|
||||||
for key, amount in iteritems(invoice_payment_amount_map):
|
|
||||||
if cancel:
|
if cancel:
|
||||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` - %s
|
frappe.db.sql("""
|
||||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
UPDATE `tabPayment Schedule`
|
||||||
|
SET
|
||||||
|
paid_amount = `paid_amount` - %s,
|
||||||
|
discounted_amount = `discounted_amount` - %s,
|
||||||
|
outstanding = `outstanding` + %s
|
||||||
|
WHERE parent = %s and payment_term = %s""",
|
||||||
|
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
|
||||||
else:
|
else:
|
||||||
outstanding = flt(invoice_paid_amount_map.get(key, {}).get('outstanding'))
|
if allocated_amount > outstanding:
|
||||||
|
|
||||||
if amount > outstanding:
|
|
||||||
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
frappe.throw(_('Cannot allocate more than {0} against payment term {1}').format(outstanding, key[0]))
|
||||||
|
|
||||||
if amount and outstanding:
|
if allocated_amount and outstanding:
|
||||||
frappe.db.sql(""" UPDATE `tabPayment Schedule` SET paid_amount = `paid_amount` + %s
|
frappe.db.sql("""
|
||||||
WHERE parent = %s and payment_term = %s""", (amount, key[1], key[0]))
|
UPDATE `tabPayment Schedule`
|
||||||
|
SET
|
||||||
|
paid_amount = `paid_amount` + %s,
|
||||||
|
discounted_amount = `discounted_amount` + %s,
|
||||||
|
outstanding = `outstanding` - %s
|
||||||
|
WHERE parent = %s and payment_term = %s""",
|
||||||
|
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]))
|
||||||
|
|
||||||
def set_status(self):
|
def set_status(self):
|
||||||
if self.docstatus == 2:
|
if self.docstatus == 2:
|
||||||
@ -708,6 +725,8 @@ def get_outstanding_reference_documents(args):
|
|||||||
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
|
||||||
args.get("party_account"), filters=args, condition=condition)
|
args.get("party_account"), filters=args, condition=condition)
|
||||||
|
|
||||||
|
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
|
||||||
|
|
||||||
for d in outstanding_invoices:
|
for d in outstanding_invoices:
|
||||||
d["exchange_rate"] = 1
|
d["exchange_rate"] = 1
|
||||||
if party_account_currency != company_currency:
|
if party_account_currency != company_currency:
|
||||||
@ -735,6 +754,46 @@ def get_outstanding_reference_documents(args):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def split_invoices_based_on_payment_terms(outstanding_invoices):
|
||||||
|
invoice_ref_based_on_payment_terms = {}
|
||||||
|
for idx, d in enumerate(outstanding_invoices):
|
||||||
|
if d.voucher_type in ['Sales Invoice', 'Purchase Invoice']:
|
||||||
|
payment_term_template = frappe.db.get_value(d.voucher_type, d.voucher_no, 'payment_terms_template')
|
||||||
|
if payment_term_template:
|
||||||
|
allocate_payment_based_on_payment_terms = frappe.db.get_value(
|
||||||
|
'Payment Terms Template', payment_term_template, 'allocate_payment_based_on_payment_terms')
|
||||||
|
if allocate_payment_based_on_payment_terms:
|
||||||
|
payment_schedule = frappe.get_all('Payment Schedule', filters={'parent': d.voucher_no}, fields=["*"])
|
||||||
|
|
||||||
|
for payment_term in payment_schedule:
|
||||||
|
if payment_term.outstanding > 0.1:
|
||||||
|
invoice_ref_based_on_payment_terms.setdefault(idx, [])
|
||||||
|
invoice_ref_based_on_payment_terms[idx].append(frappe._dict({
|
||||||
|
'due_date': d.due_date,
|
||||||
|
'currency': d.currency,
|
||||||
|
'voucher_no': d.voucher_no,
|
||||||
|
'voucher_type': d.voucher_type,
|
||||||
|
'posting_date': d.posting_date,
|
||||||
|
'invoice_amount': flt(d.invoice_amount),
|
||||||
|
'outstanding_amount': flt(d.outstanding_amount),
|
||||||
|
'payment_amount': payment_term.payment_amount,
|
||||||
|
'payment_term': payment_term.payment_term,
|
||||||
|
'allocated_amount': payment_term.outstanding
|
||||||
|
}))
|
||||||
|
|
||||||
|
if invoice_ref_based_on_payment_terms:
|
||||||
|
for idx, ref in invoice_ref_based_on_payment_terms.items():
|
||||||
|
voucher_no = outstanding_invoices[idx]['voucher_no']
|
||||||
|
voucher_type = outstanding_invoices[idx]['voucher_type']
|
||||||
|
|
||||||
|
frappe.msgprint(_("Spliting {} {} into {} rows as per payment terms").format(
|
||||||
|
voucher_type, voucher_no, len(ref)), alert=True)
|
||||||
|
|
||||||
|
outstanding_invoices.pop(idx - 1)
|
||||||
|
outstanding_invoices += invoice_ref_based_on_payment_terms[idx]
|
||||||
|
|
||||||
|
return outstanding_invoices
|
||||||
|
|
||||||
def get_orders_to_be_billed(posting_date, party_type, party,
|
def get_orders_to_be_billed(posting_date, party_type, party,
|
||||||
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
company, party_account_currency, company_currency, cost_center=None, filters=None):
|
||||||
if party_type == "Customer":
|
if party_type == "Customer":
|
||||||
@ -1091,6 +1150,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
paid_amount, received_amount = set_paid_amount_and_received_amount(
|
paid_amount, received_amount = set_paid_amount_and_received_amount(
|
||||||
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
|
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc)
|
||||||
|
|
||||||
|
paid_amount, received_amount, discount_amount = apply_early_payment_discount(paid_amount, received_amount, doc)
|
||||||
|
|
||||||
pe = frappe.new_doc("Payment Entry")
|
pe = frappe.new_doc("Payment Entry")
|
||||||
pe.payment_type = payment_type
|
pe.payment_type = payment_type
|
||||||
pe.company = doc.company
|
pe.company = doc.company
|
||||||
@ -1160,11 +1221,20 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
|
|||||||
|
|
||||||
pe.setup_party_account_field()
|
pe.setup_party_account_field()
|
||||||
pe.set_missing_values()
|
pe.set_missing_values()
|
||||||
|
|
||||||
if party_account and bank:
|
if party_account and bank:
|
||||||
if dt == "Employee Advance":
|
if dt == "Employee Advance":
|
||||||
reference_doc = doc
|
reference_doc = doc
|
||||||
pe.set_exchange_rate(ref_doc=reference_doc)
|
pe.set_exchange_rate(ref_doc=reference_doc)
|
||||||
pe.set_amounts()
|
pe.set_amounts()
|
||||||
|
if discount_amount:
|
||||||
|
pe.set_gain_or_loss(account_details={
|
||||||
|
'account': frappe.get_cached_value('Company', pe.company, "default_discount_account"),
|
||||||
|
'cost_center': pe.cost_center or frappe.get_cached_value('Company', pe.company, "cost_center"),
|
||||||
|
'amount': discount_amount * (-1 if payment_type == "Pay" else 1)
|
||||||
|
})
|
||||||
|
pe.set_difference_amount()
|
||||||
|
|
||||||
return pe
|
return pe
|
||||||
|
|
||||||
def get_bank_cash_account(doc, bank_account):
|
def get_bank_cash_account(doc, bank_account):
|
||||||
@ -1285,6 +1355,33 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta
|
|||||||
paid_amount = received_amount * doc.get('exchange_rate', 1)
|
paid_amount = received_amount * 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):
|
||||||
|
total_discount = 0
|
||||||
|
if doc.doctype in ['Sales Invoice', 'Purchase Invoice'] and doc.payment_schedule:
|
||||||
|
for term in doc.payment_schedule:
|
||||||
|
if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
|
||||||
|
if term.discount_type == 'Percentage':
|
||||||
|
discount_amount = flt(doc.get('grand_total')) * (term.discount / 100)
|
||||||
|
else:
|
||||||
|
discount_amount = term.discount
|
||||||
|
|
||||||
|
discount_amount_in_foreign_currency = discount_amount * doc.get('conversion_rate', 1)
|
||||||
|
|
||||||
|
if doc.doctype == 'Sales Invoice':
|
||||||
|
paid_amount -= discount_amount
|
||||||
|
received_amount -= discount_amount_in_foreign_currency
|
||||||
|
else:
|
||||||
|
received_amount -= discount_amount
|
||||||
|
paid_amount -= discount_amount_in_foreign_currency
|
||||||
|
|
||||||
|
total_discount += discount_amount
|
||||||
|
|
||||||
|
if total_discount:
|
||||||
|
money = frappe.utils.fmt_money(total_discount, currency=doc.get('currency'))
|
||||||
|
frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1)
|
||||||
|
|
||||||
|
return paid_amount, received_amount, total_discount
|
||||||
|
|
||||||
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
def get_reference_as_per_payment_terms(payment_schedule, dt, dn, doc, grand_total, outstanding_amount):
|
||||||
references = []
|
references = []
|
||||||
for payment_term in payment_schedule:
|
for payment_term in payment_schedule:
|
||||||
|
@ -193,6 +193,34 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
|
||||||
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
|
||||||
|
|
||||||
|
def test_payment_entry_against_payment_terms_with_discount(self):
|
||||||
|
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
|
||||||
|
create_payment_terms_template_with_discount()
|
||||||
|
si.payment_terms_template = 'Test Discount Template'
|
||||||
|
|
||||||
|
frappe.db.set_value('Company', si.company, 'default_discount_account', 'Write Off - _TC')
|
||||||
|
|
||||||
|
si.append('taxes', {
|
||||||
|
"charge_type": "On Net Total",
|
||||||
|
"account_head": "_Test Account Service Tax - _TC",
|
||||||
|
"cost_center": "_Test Cost Center - _TC",
|
||||||
|
"description": "Service Tax",
|
||||||
|
"rate": 18
|
||||||
|
})
|
||||||
|
si.save()
|
||||||
|
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
|
||||||
|
pe.submit()
|
||||||
|
si.load_from_db()
|
||||||
|
|
||||||
|
self.assertEqual(pe.references[0].payment_term, '30 Credit Days with 10% Discount')
|
||||||
|
self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
|
||||||
|
self.assertEqual(si.payment_schedule[0].paid_amount, 212.40)
|
||||||
|
self.assertEqual(si.payment_schedule[0].outstanding, 0)
|
||||||
|
self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
|
||||||
|
|
||||||
|
|
||||||
def test_payment_against_purchase_invoice_to_check_status(self):
|
def test_payment_against_purchase_invoice_to_check_status(self):
|
||||||
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
|
||||||
@ -591,6 +619,26 @@ def create_payment_terms_template():
|
|||||||
}]
|
}]
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
def create_payment_terms_template_with_discount():
|
||||||
|
|
||||||
|
create_payment_term('30 Credit Days with 10% Discount')
|
||||||
|
|
||||||
|
if not frappe.db.exists('Payment Terms Template', 'Test Discount Template'):
|
||||||
|
payment_term_template = frappe.get_doc({
|
||||||
|
'doctype': 'Payment Terms Template',
|
||||||
|
'template_name': 'Test Discount Template',
|
||||||
|
'allocate_payment_based_on_payment_terms': 1,
|
||||||
|
'terms': [{
|
||||||
|
'doctype': 'Payment Terms Template Detail',
|
||||||
|
'payment_term': '30 Credit Days with 10% Discount',
|
||||||
|
'invoice_portion': 100,
|
||||||
|
'credit_days_based_on': 'Day(s) after invoice date',
|
||||||
|
'credit_days': 2,
|
||||||
|
'discount': 10,
|
||||||
|
'discount_validity_based_on': 'Day(s) after invoice date',
|
||||||
|
'discount_validity': 1
|
||||||
|
}]
|
||||||
|
}).insert()
|
||||||
|
|
||||||
def create_payment_term(name):
|
def create_payment_term(name):
|
||||||
if not frappe.db.exists('Payment Term', name):
|
if not frappe.db.exists('Payment Term', name):
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
"fieldname": "total_amount",
|
"fieldname": "total_amount",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Total Amount",
|
"label": "Grand Total",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -92,9 +92,10 @@
|
|||||||
"options": "Payment Term"
|
"options": "Payment Term"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-13 12:07:19.362539",
|
"modified": "2021-02-10 11:25:47.144392",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry Reference",
|
"name": "Payment Entry Reference",
|
||||||
|
@ -11,6 +11,7 @@ from erpnext.accounts.utils import (get_outstanding_invoices,
|
|||||||
from erpnext.controllers.accounts_controller import get_advance_payment_entries
|
from erpnext.controllers.accounts_controller import get_advance_payment_entries
|
||||||
|
|
||||||
class PaymentReconciliation(Document):
|
class PaymentReconciliation(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_unreconciled_entries(self):
|
def get_unreconciled_entries(self):
|
||||||
self.get_nonreconciled_payment_entries()
|
self.get_nonreconciled_payment_entries()
|
||||||
self.get_invoice_entries()
|
self.get_invoice_entries()
|
||||||
@ -147,6 +148,7 @@ class PaymentReconciliation(Document):
|
|||||||
ent.currency = e.get('currency')
|
ent.currency = e.get('currency')
|
||||||
ent.outstanding_amount = e.get('outstanding_amount')
|
ent.outstanding_amount = e.get('outstanding_amount')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reconcile(self, args):
|
def reconcile(self, args):
|
||||||
for e in self.get('payments'):
|
for e in self.get('payments'):
|
||||||
e.invoice_type = None
|
e.invoice_type = None
|
||||||
@ -197,6 +199,7 @@ class PaymentReconciliation(Document):
|
|||||||
'difference_account': row.difference_account
|
'difference_account': row.difference_account
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_difference_amount(self, child_row):
|
def get_difference_amount(self, child_row):
|
||||||
if child_row.get("reference_type") != 'Payment Entry': return
|
if child_row.get("reference_type") != 'Payment Entry': return
|
||||||
|
|
||||||
|
@ -6,11 +6,23 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"payment_term",
|
"payment_term",
|
||||||
|
"section_break_15",
|
||||||
"description",
|
"description",
|
||||||
|
"section_break_4",
|
||||||
"due_date",
|
"due_date",
|
||||||
"invoice_portion",
|
|
||||||
"payment_amount",
|
|
||||||
"mode_of_payment",
|
"mode_of_payment",
|
||||||
|
"column_break_5",
|
||||||
|
"invoice_portion",
|
||||||
|
"section_break_6",
|
||||||
|
"discount_type",
|
||||||
|
"discount_date",
|
||||||
|
"column_break_9",
|
||||||
|
"discount",
|
||||||
|
"section_break_9",
|
||||||
|
"payment_amount",
|
||||||
|
"discounted_amount",
|
||||||
|
"column_break_3",
|
||||||
|
"outstanding",
|
||||||
"paid_amount"
|
"paid_amount"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -25,6 +37,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 2,
|
"columns": 2,
|
||||||
|
"fetch_from": "payment_term.description",
|
||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -62,14 +75,82 @@
|
|||||||
"options": "Mode of Payment"
|
"options": "Mode of Payment"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "paid_amount",
|
||||||
"fieldname": "paid_amount",
|
"fieldname": "paid_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Paid Amount"
|
"label": "Paid Amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"depends_on": "discounted_amount",
|
||||||
|
"fieldname": "discounted_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Discounted Amount",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "payment_amount",
|
||||||
|
"fieldname": "outstanding",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Outstanding",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_5",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"label": "Discount Date",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Percentage",
|
||||||
|
"fetch_from": "payment_term.discount_type",
|
||||||
|
"fieldname": "discount_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Type",
|
||||||
|
"options": "Percentage\nAmount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "payment_term.discount",
|
||||||
|
"fieldname": "discount",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_9",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_15",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_6",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_4",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-13 17:58:24.729526",
|
"modified": "2021-02-15 21:03:12.540546",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Schedule",
|
"name": "Payment Schedule",
|
||||||
|
@ -1,2 +1,22 @@
|
|||||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
frappe.ui.form.on('Payment Term', {
|
||||||
|
onload(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
discount(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
discount_type(frm) {
|
||||||
|
frm.trigger('set_dynamic_description');
|
||||||
|
},
|
||||||
|
set_dynamic_description(frm) {
|
||||||
|
if (frm.doc.discount) {
|
||||||
|
let description = __("{0}% of total invoice value will be given as discount.", [frm.doc.discount]);
|
||||||
|
if (frm.doc.discount_type == 'Amount') {
|
||||||
|
description = __("{0} will be given as discount.", [fmt_money(frm.doc.discount)]);
|
||||||
|
}
|
||||||
|
frm.set_df_property("discount", "description", description);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
@ -1,386 +1,166 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"allow_import": 1,
|
||||||
"allow_import": 1,
|
"allow_rename": 1,
|
||||||
"allow_rename": 1,
|
"autoname": "field:payment_term_name",
|
||||||
"autoname": "field:payment_term_name",
|
"creation": "2017-08-10 15:24:54.876365",
|
||||||
"beta": 0,
|
"doctype": "DocType",
|
||||||
"creation": "2017-08-10 15:24:54.876365",
|
"editable_grid": 1,
|
||||||
"custom": 0,
|
"engine": "InnoDB",
|
||||||
"docstatus": 0,
|
"field_order": [
|
||||||
"doctype": "DocType",
|
"payment_term_name",
|
||||||
"document_type": "",
|
"invoice_portion",
|
||||||
"editable_grid": 1,
|
"mode_of_payment",
|
||||||
"engine": "InnoDB",
|
"column_break_3",
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"section_break_8",
|
||||||
|
"discount_type",
|
||||||
|
"discount",
|
||||||
|
"column_break_11",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity",
|
||||||
|
"section_break_6",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"bold": 1,
|
||||||
"allow_on_submit": 0,
|
"fieldname": "payment_term_name",
|
||||||
"bold": 1,
|
"fieldtype": "Data",
|
||||||
"collapsible": 0,
|
"label": "Payment Term Name",
|
||||||
"columns": 0,
|
"unique": 1
|
||||||
"fieldname": "payment_term_name",
|
},
|
||||||
"fieldtype": "Data",
|
|
||||||
"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": "Payment Term Name",
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Provide the invoice portion in percent",
|
"bold": 1,
|
||||||
"allow_bulk_edit": 0,
|
"fieldname": "invoice_portion",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Float",
|
||||||
"bold": 1,
|
"label": "Invoice Portion (%)"
|
||||||
"collapsible": 0,
|
},
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "invoice_portion",
|
|
||||||
"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": "Invoice Portion",
|
|
||||||
"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,
|
"fieldname": "mode_of_payment",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"label": "Mode of Payment",
|
||||||
"collapsible": 0,
|
"options": "Mode of Payment"
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "mode_of_payment",
|
|
||||||
"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": "Mode of Payment",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Mode of Payment",
|
|
||||||
"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,
|
"fieldname": "column_break_3",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Column Break"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
|
||||||
"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,
|
"bold": 1,
|
||||||
"allow_on_submit": 0,
|
"fieldname": "due_date_based_on",
|
||||||
"bold": 1,
|
"fieldtype": "Select",
|
||||||
"collapsible": 0,
|
"label": "Due Date Based On",
|
||||||
"columns": 0,
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
"fieldname": "due_date_based_on",
|
},
|
||||||
"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": "Due Date Based On",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"description": "Give number of days according to prior selection",
|
"bold": 1,
|
||||||
"allow_bulk_edit": 0,
|
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "credit_days",
|
||||||
"bold": 1,
|
"fieldtype": "Int",
|
||||||
"collapsible": 0,
|
"label": "Credit Days"
|
||||||
"columns": 0,
|
},
|
||||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
|
||||||
"fieldname": "credit_days",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Credit Days",
|
|
||||||
"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,
|
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "credit_months",
|
||||||
"bold": 0,
|
"fieldtype": "Int",
|
||||||
"collapsible": 0,
|
"label": "Credit Months"
|
||||||
"columns": 0,
|
},
|
||||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
|
||||||
"fieldname": "credit_months",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Credit Months",
|
|
||||||
"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,
|
"fieldname": "section_break_6",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Section Break"
|
||||||
"bold": 0,
|
},
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "section_break_6",
|
|
||||||
"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,
|
|
||||||
"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,
|
"bold": 1,
|
||||||
"allow_on_submit": 0,
|
"fieldname": "description",
|
||||||
"bold": 1,
|
"fieldtype": "Small Text",
|
||||||
"collapsible": 0,
|
"label": "Description"
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "description",
|
{
|
||||||
"fieldtype": "Small Text",
|
"fieldname": "section_break_8",
|
||||||
"hidden": 0,
|
"fieldtype": "Section Break",
|
||||||
"ignore_user_permissions": 0,
|
"label": "Discount Settings"
|
||||||
"ignore_xss_filter": 0,
|
},
|
||||||
"in_filter": 0,
|
{
|
||||||
"in_global_search": 0,
|
"default": "Percentage",
|
||||||
"in_list_view": 0,
|
"fieldname": "discount_type",
|
||||||
"in_standard_filter": 0,
|
"fieldtype": "Select",
|
||||||
"label": "Description",
|
"label": "Discount Type",
|
||||||
"length": 0,
|
"options": "Percentage\nAmount"
|
||||||
"no_copy": 0,
|
},
|
||||||
"permlevel": 0,
|
{
|
||||||
"precision": "",
|
"fieldname": "discount",
|
||||||
"print_hide": 0,
|
"fieldtype": "Float",
|
||||||
"print_hide_if_no_value": 0,
|
"label": "Discount"
|
||||||
"read_only": 0,
|
},
|
||||||
"remember_last_selected_value": 0,
|
{
|
||||||
"report_hide": 0,
|
"default": "Day(s) after invoice date",
|
||||||
"reqd": 0,
|
"depends_on": "discount",
|
||||||
"search_index": 0,
|
"fieldname": "discount_validity_based_on",
|
||||||
"set_only_once": 0,
|
"fieldtype": "Select",
|
||||||
"translatable": 0,
|
"label": "Discount Validity Based On",
|
||||||
"unique": 0
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fieldname": "discount_validity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Discount Validity",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"links": [],
|
||||||
"hide_heading": 0,
|
"modified": "2021-02-15 20:30:56.256403",
|
||||||
"hide_toolbar": 0,
|
"modified_by": "Administrator",
|
||||||
"idx": 0,
|
"module": "Accounts",
|
||||||
"image_view": 0,
|
"name": "Payment Term",
|
||||||
"in_create": 0,
|
"owner": "Administrator",
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2020-10-14 10:47:32.830478",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Accounts",
|
|
||||||
"name": "Payment Term",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "System Manager",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "Accounts Manager",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts Manager",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"amend": 0,
|
"create": 1,
|
||||||
"apply_user_permissions": 0,
|
"delete": 1,
|
||||||
"cancel": 0,
|
"email": 1,
|
||||||
"create": 1,
|
"export": 1,
|
||||||
"delete": 1,
|
"print": 1,
|
||||||
"email": 1,
|
"read": 1,
|
||||||
"export": 1,
|
"report": 1,
|
||||||
"if_owner": 0,
|
"role": "Accounts User",
|
||||||
"import": 0,
|
"share": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "Accounts User",
|
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
"sort_field": "modified",
|
||||||
"read_only_onload": 0,
|
"sort_order": "DESC",
|
||||||
"show_name_in_global_search": 0,
|
"track_changes": 1
|
||||||
"sort_field": "modified",
|
}
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
|
@ -3,11 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Payment Terms Template', {
|
frappe.ui.form.on('Payment Terms Template', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("payment_term", "description", "description");
|
|
||||||
frm.add_fetch("payment_term", "invoice_portion", "invoice_portion");
|
|
||||||
frm.add_fetch("payment_term", "due_date_based_on", "due_date_based_on");
|
|
||||||
frm.add_fetch("payment_term", "credit_days", "credit_days");
|
|
||||||
frm.add_fetch("payment_term", "credit_months", "credit_months");
|
|
||||||
frm.add_fetch("payment_term", "mode_of_payment", "mode_of_payment");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,6 @@ from frappe import _
|
|||||||
class PaymentTermsTemplate(Document):
|
class PaymentTermsTemplate(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_invoice_portion()
|
self.validate_invoice_portion()
|
||||||
self.validate_credit_days()
|
|
||||||
self.check_duplicate_terms()
|
self.check_duplicate_terms()
|
||||||
|
|
||||||
def validate_invoice_portion(self):
|
def validate_invoice_portion(self):
|
||||||
@ -24,11 +23,6 @@ class PaymentTermsTemplate(Document):
|
|||||||
if flt(total_portion, 2) != 100.00:
|
if flt(total_portion, 2) != 100.00:
|
||||||
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
frappe.msgprint(_('Combined invoice portion must equal 100%'), raise_exception=1, indicator='red')
|
||||||
|
|
||||||
def validate_credit_days(self):
|
|
||||||
for term in self.terms:
|
|
||||||
if cint(term.credit_days) < 0:
|
|
||||||
frappe.msgprint(_('Credit Days cannot be a negative number'), raise_exception=1, indicator='red')
|
|
||||||
|
|
||||||
def check_duplicate_terms(self):
|
def check_duplicate_terms(self):
|
||||||
terms = []
|
terms = []
|
||||||
for term in self.terms:
|
for term in self.terms:
|
||||||
|
@ -1,278 +1,164 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 0,
|
"creation": "2017-08-10 15:34:09.409562",
|
||||||
"allow_import": 0,
|
"doctype": "DocType",
|
||||||
"allow_rename": 0,
|
"editable_grid": 1,
|
||||||
"autoname": "",
|
"engine": "InnoDB",
|
||||||
"beta": 0,
|
"field_order": [
|
||||||
"creation": "2017-08-10 15:34:09.409562",
|
"payment_term",
|
||||||
"custom": 0,
|
"section_break_13",
|
||||||
"docstatus": 0,
|
"description",
|
||||||
"doctype": "DocType",
|
"section_break_4",
|
||||||
"document_type": "",
|
"invoice_portion",
|
||||||
"editable_grid": 1,
|
"mode_of_payment",
|
||||||
"engine": "InnoDB",
|
"column_break_3",
|
||||||
|
"due_date_based_on",
|
||||||
|
"credit_days",
|
||||||
|
"credit_months",
|
||||||
|
"section_break_8",
|
||||||
|
"discount_type",
|
||||||
|
"discount",
|
||||||
|
"column_break_11",
|
||||||
|
"discount_validity_based_on",
|
||||||
|
"discount_validity"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "payment_term",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"in_list_view": 1,
|
||||||
"collapsible": 0,
|
"label": "Payment Term",
|
||||||
"columns": 2,
|
"options": "Payment Term"
|
||||||
"fieldname": "payment_term",
|
},
|
||||||
"fieldtype": "Link",
|
|
||||||
"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": "Payment Term",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Payment Term",
|
|
||||||
"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,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fetch_from": "payment_term.description",
|
||||||
"allow_on_submit": 0,
|
"fieldname": "description",
|
||||||
"bold": 0,
|
"fieldtype": "Small Text",
|
||||||
"collapsible": 0,
|
"in_list_view": 1,
|
||||||
"columns": 2,
|
"label": "Description"
|
||||||
"fieldname": "description",
|
},
|
||||||
"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_standard_filter": 0,
|
|
||||||
"label": "Description",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fetch_from": "payment_term.invoice_portion",
|
||||||
"allow_on_submit": 0,
|
"fetch_if_empty": 1,
|
||||||
"bold": 0,
|
"fieldname": "invoice_portion",
|
||||||
"collapsible": 0,
|
"fieldtype": "Float",
|
||||||
"columns": 2,
|
"in_list_view": 1,
|
||||||
"default": "0",
|
"label": "Invoice Portion (%)",
|
||||||
"fieldname": "invoice_portion",
|
"reqd": 1
|
||||||
"fieldtype": "Percent",
|
},
|
||||||
"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": "Invoice Portion",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"fetch_from": "payment_term.due_date_based_on",
|
||||||
"allow_on_submit": 0,
|
"fetch_if_empty": 1,
|
||||||
"bold": 0,
|
"fieldname": "due_date_based_on",
|
||||||
"collapsible": 0,
|
"fieldtype": "Select",
|
||||||
"columns": 2,
|
"in_list_view": 1,
|
||||||
"fieldname": "due_date_based_on",
|
"label": "Due Date Based On",
|
||||||
"fieldtype": "Select",
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
||||||
"hidden": 0,
|
"reqd": 1
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Due Date Based On",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month",
|
|
||||||
"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,
|
"columns": 2,
|
||||||
"allow_in_quick_entry": 0,
|
"default": "0",
|
||||||
"allow_on_submit": 0,
|
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
||||||
"bold": 0,
|
"fetch_from": "payment_term.credit_days",
|
||||||
"collapsible": 0,
|
"fetch_if_empty": 1,
|
||||||
"columns": 2,
|
"fieldname": "credit_days",
|
||||||
"default": "0",
|
"fieldtype": "Int",
|
||||||
"depends_on": "eval:in_list(['Day(s) after invoice date', 'Day(s) after the end of the invoice month'], doc.due_date_based_on)",
|
"in_list_view": 1,
|
||||||
"fieldname": "credit_days",
|
"label": "Credit Days",
|
||||||
"fieldtype": "Int",
|
"non_negative": 1
|
||||||
"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": "Credit Days",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "",
|
|
||||||
"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,
|
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
||||||
"allow_on_submit": 0,
|
"fetch_from": "payment_term.credit_months",
|
||||||
"bold": 0,
|
"fetch_if_empty": 1,
|
||||||
"collapsible": 0,
|
"fieldname": "credit_months",
|
||||||
"columns": 0,
|
"fieldtype": "Int",
|
||||||
"default": "0",
|
"label": "Credit Months",
|
||||||
"depends_on": "eval:doc.due_date_based_on=='Month(s) after the end of the invoice month'",
|
"non_negative": 1
|
||||||
"fieldname": "credit_months",
|
},
|
||||||
"fieldtype": "Int",
|
|
||||||
"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": "Credit Months",
|
|
||||||
"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,
|
"fetch_from": "payment_term.mode_of_payment",
|
||||||
"allow_in_quick_entry": 0,
|
"fieldname": "mode_of_payment",
|
||||||
"allow_on_submit": 0,
|
"fieldtype": "Link",
|
||||||
"bold": 0,
|
"label": "Mode of Payment",
|
||||||
"collapsible": 0,
|
"options": "Mode of Payment"
|
||||||
"columns": 0,
|
},
|
||||||
"fieldname": "mode_of_payment",
|
{
|
||||||
"fieldtype": "Link",
|
"fieldname": "column_break_3",
|
||||||
"hidden": 0,
|
"fieldtype": "Column Break"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"fieldname": "section_break_8",
|
||||||
"in_global_search": 0,
|
"fieldtype": "Section Break",
|
||||||
"in_list_view": 0,
|
"label": "Discount Settings"
|
||||||
"in_standard_filter": 0,
|
},
|
||||||
"label": "Mode of Payment",
|
{
|
||||||
"length": 0,
|
"default": "Percentage",
|
||||||
"no_copy": 0,
|
"fetch_from": "payment_term.discount_type",
|
||||||
"options": "Mode of Payment",
|
"fetch_if_empty": 1,
|
||||||
"permlevel": 0,
|
"fieldname": "discount_type",
|
||||||
"precision": "",
|
"fieldtype": "Select",
|
||||||
"print_hide": 0,
|
"label": "Discount Type",
|
||||||
"print_hide_if_no_value": 0,
|
"options": "Percentage\nAmount"
|
||||||
"read_only": 0,
|
},
|
||||||
"remember_last_selected_value": 0,
|
{
|
||||||
"report_hide": 0,
|
"fetch_from": "payment_term.discount",
|
||||||
"reqd": 0,
|
"fetch_if_empty": 1,
|
||||||
"search_index": 0,
|
"fieldname": "discount",
|
||||||
"set_only_once": 0,
|
"fieldtype": "Float",
|
||||||
"translatable": 0,
|
"label": "Discount"
|
||||||
"unique": 0
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_11",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Day(s) after invoice date",
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fetch_from": "payment_term.discount_validity_based_on",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount_validity_based_on",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Discount Validity Based On",
|
||||||
|
"options": "Day(s) after invoice date\nDay(s) after the end of the invoice month\nMonth(s) after the end of the invoice month"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_13",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "discount",
|
||||||
|
"fetch_from": "payment_term.discount_validity",
|
||||||
|
"fetch_if_empty": 1,
|
||||||
|
"fieldname": "discount_validity",
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": "Discount Validity",
|
||||||
|
"mandatory_depends_on": "discount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_4",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
"index_web_pages_for_search": 1,
|
||||||
"hide_heading": 0,
|
"istable": 1,
|
||||||
"hide_toolbar": 0,
|
"links": [],
|
||||||
"idx": 0,
|
"modified": "2021-02-24 11:56:12.410807",
|
||||||
"image_view": 0,
|
"modified_by": "Administrator",
|
||||||
"in_create": 0,
|
"module": "Accounts",
|
||||||
"is_submittable": 0,
|
"name": "Payment Terms Template Detail",
|
||||||
"issingle": 0,
|
"owner": "Administrator",
|
||||||
"istable": 1,
|
"permissions": [],
|
||||||
"max_attachments": 0,
|
"quick_entry": 1,
|
||||||
"modified": "2018-08-21 16:15:55.143025",
|
"sort_field": "modified",
|
||||||
"modified_by": "Administrator",
|
"sort_order": "DESC",
|
||||||
"module": "Accounts",
|
"track_changes": 1
|
||||||
"name": "Payment Terms Template Detail",
|
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [],
|
|
||||||
"quick_entry": 1,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC",
|
|
||||||
"track_changes": 1,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -18,7 +18,7 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
|
|
||||||
self.validate_pos_closing()
|
self.validate_pos_closing()
|
||||||
self.validate_pos_invoices()
|
self.validate_pos_invoices()
|
||||||
|
|
||||||
def validate_pos_closing(self):
|
def validate_pos_closing(self):
|
||||||
user = frappe.db.sql("""
|
user = frappe.db.sql("""
|
||||||
SELECT name FROM `tabPOS Closing Entry`
|
SELECT name FROM `tabPOS Closing Entry`
|
||||||
@ -37,12 +37,12 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
bold_user = frappe.bold(self.user)
|
bold_user = frappe.bold(self.user)
|
||||||
frappe.throw(_("POS Closing Entry {} against {} between selected period")
|
frappe.throw(_("POS Closing Entry {} against {} between selected period")
|
||||||
.format(bold_already_exists, bold_user), title=_("Invalid Period"))
|
.format(bold_already_exists, bold_user), title=_("Invalid Period"))
|
||||||
|
|
||||||
def validate_pos_invoices(self):
|
def validate_pos_invoices(self):
|
||||||
invalid_rows = []
|
invalid_rows = []
|
||||||
for d in self.pos_transactions:
|
for d in self.pos_transactions:
|
||||||
invalid_row = {'idx': d.idx}
|
invalid_row = {'idx': d.idx}
|
||||||
pos_invoice = frappe.db.get_values("POS Invoice", d.pos_invoice,
|
pos_invoice = frappe.db.get_values("POS Invoice", d.pos_invoice,
|
||||||
["consolidated_invoice", "pos_profile", "docstatus", "owner"], as_dict=1)[0]
|
["consolidated_invoice", "pos_profile", "docstatus", "owner"], as_dict=1)[0]
|
||||||
if pos_invoice.consolidated_invoice:
|
if pos_invoice.consolidated_invoice:
|
||||||
invalid_row.setdefault('msg', []).append(_('POS Invoice is {}').format(frappe.bold("already consolidated")))
|
invalid_row.setdefault('msg', []).append(_('POS Invoice is {}').format(frappe.bold("already consolidated")))
|
||||||
@ -68,14 +68,15 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
|
|
||||||
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_payment_reconciliation_details(self):
|
def get_payment_reconciliation_details(self):
|
||||||
currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||||
return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
|
return frappe.render_template("erpnext/accounts/doctype/pos_closing_entry/closing_voucher_details.html",
|
||||||
{"data": self, "currency": currency})
|
{"data": self, "currency": currency})
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
consolidate_pos_invoices(closing_entry=self)
|
consolidate_pos_invoices(closing_entry=self)
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
unconsolidate_pos_invoices(closing_entry=self)
|
unconsolidate_pos_invoices(closing_entry=self)
|
||||||
|
|
||||||
|
@ -5,12 +5,21 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
|
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||||
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
|
from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
|
||||||
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import make_closing_entry_from_opening
|
from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import make_closing_entry_from_opening
|
||||||
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
|
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
|
||||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||||
|
|
||||||
class TestPOSClosingEntry(unittest.TestCase):
|
class TestPOSClosingEntry(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Make stock available for POS Sales
|
||||||
|
make_stock_entry(target="_Test Warehouse - _TC", qty=2, basic_rate=100)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
|
||||||
def test_pos_closing_entry(self):
|
def test_pos_closing_entry(self):
|
||||||
test_user, pos_profile = init_user_and_profile()
|
test_user, pos_profile = init_user_and_profile()
|
||||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||||
@ -41,9 +50,6 @@ class TestPOSClosingEntry(unittest.TestCase):
|
|||||||
self.assertEqual(pcv_doc.total_quantity, 2)
|
self.assertEqual(pcv_doc.total_quantity, 2)
|
||||||
self.assertEqual(pcv_doc.net_total, 6700)
|
self.assertEqual(pcv_doc.net_total, 6700)
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
|
||||||
|
|
||||||
def test_cancelling_of_pos_closing_entry(self):
|
def test_cancelling_of_pos_closing_entry(self):
|
||||||
test_user, pos_profile = init_user_and_profile()
|
test_user, pos_profile = init_user_and_profile()
|
||||||
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
opening_entry = create_opening_entry(pos_profile, test_user.name)
|
||||||
@ -84,8 +90,6 @@ class TestPOSClosingEntry(unittest.TestCase):
|
|||||||
self.assertEqual(si_doc.docstatus, 2)
|
self.assertEqual(si_doc.docstatus, 2)
|
||||||
self.assertEqual(pos_inv1.status, 'Paid')
|
self.assertEqual(pos_inv1.status, 'Paid')
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
|
||||||
|
|
||||||
def init_user_and_profile(**args):
|
def init_user_and_profile(**args):
|
||||||
user = 'test@example.com'
|
user = 'test@example.com'
|
||||||
@ -103,4 +107,4 @@ def init_user_and_profile(**args):
|
|||||||
|
|
||||||
pos_profile.save()
|
pos_profile.save()
|
||||||
|
|
||||||
return test_user, pos_profile
|
return test_user, pos_profile
|
||||||
|
@ -57,7 +57,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
self.apply_loyalty_points()
|
self.apply_loyalty_points()
|
||||||
self.check_phone_payments()
|
self.check_phone_payments()
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
|
|
||||||
def before_cancel(self):
|
def before_cancel(self):
|
||||||
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
||||||
pos_closing_entry = frappe.get_all(
|
pos_closing_entry = frappe.get_all(
|
||||||
@ -221,7 +221,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
base_grand_total = flt(self.base_rounded_total) or flt(self.base_grand_total)
|
base_grand_total = flt(self.base_rounded_total) or flt(self.base_grand_total)
|
||||||
if not flt(self.change_amount) and grand_total < flt(self.paid_amount):
|
if not flt(self.change_amount) and grand_total < flt(self.paid_amount):
|
||||||
self.change_amount = flt(self.paid_amount - grand_total + flt(self.write_off_amount))
|
self.change_amount = flt(self.paid_amount - grand_total + flt(self.write_off_amount))
|
||||||
self.base_change_amount = flt(self.base_paid_amount - base_grand_total + flt(self.base_write_off_amount))
|
self.base_change_amount = flt(self.base_paid_amount) - base_grand_total + flt(self.base_write_off_amount)
|
||||||
|
|
||||||
if flt(self.change_amount) and not self.account_for_change_amount:
|
if flt(self.change_amount) and not self.account_for_change_amount:
|
||||||
frappe.msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
|
frappe.msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
|
||||||
@ -355,6 +355,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
return profile
|
return profile
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate=False):
|
||||||
profile = self.set_pos_fields(for_validate)
|
profile = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
@ -377,12 +378,20 @@ class POSInvoice(SalesInvoice):
|
|||||||
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def reset_mode_of_payments(self):
|
||||||
|
if self.pos_profile:
|
||||||
|
pos_profile = frappe.get_cached_doc('POS Profile', self.pos_profile)
|
||||||
|
update_multi_mode_option(self, pos_profile)
|
||||||
|
self.paid_amount = 0
|
||||||
|
|
||||||
def set_account_for_mode_of_payment(self):
|
def set_account_for_mode_of_payment(self):
|
||||||
self.payments = [d for d in self.payments if d.amount or d.base_amount or d.default]
|
self.payments = [d for d in self.payments if d.amount or d.base_amount or d.default]
|
||||||
for pay in self.payments:
|
for pay in self.payments:
|
||||||
if not pay.account:
|
if not pay.account:
|
||||||
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
|
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_payment_request(self):
|
def create_payment_request(self):
|
||||||
for pay in self.payments:
|
for pay in self.payments:
|
||||||
if pay.type == "Phone":
|
if pay.type == "Phone":
|
||||||
@ -400,7 +409,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
pay_req.request_phone_payment()
|
pay_req.request_phone_payment()
|
||||||
|
|
||||||
return pay_req
|
return pay_req
|
||||||
|
|
||||||
def get_new_payment_request(self, mop):
|
def get_new_payment_request(self, mop):
|
||||||
payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
|
payment_gateway_account = frappe.db.get_value("Payment Gateway Account", {
|
||||||
"payment_account": mop.account,
|
"payment_account": mop.account,
|
||||||
|
@ -9,8 +9,20 @@ from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profi
|
|||||||
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||||
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
|
|
||||||
class TestPOSInvoice(unittest.TestCase):
|
class TestPOSInvoice(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
frappe.db.sql("delete from `tabTax Rule`")
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if frappe.session.user != "Administrator":
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
if frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||||
|
frappe.db.set_value("Selling Settings", None, "validate_selling_price", 0)
|
||||||
|
|
||||||
def test_timestamp_change(self):
|
def test_timestamp_change(self):
|
||||||
w = create_pos_invoice(do_not_save=1)
|
w = create_pos_invoice(do_not_save=1)
|
||||||
w.docstatus = 0
|
w.docstatus = 0
|
||||||
@ -370,7 +382,6 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
pos_inv.load_from_db()
|
pos_inv.load_from_db()
|
||||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||||
self.assertEqual(rounded_total, 3470)
|
self.assertEqual(rounded_total, 3470)
|
||||||
frappe.set_user("Administrator")
|
|
||||||
|
|
||||||
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
|
||||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||||
@ -412,7 +423,6 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
pos_inv.load_from_db()
|
pos_inv.load_from_db()
|
||||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||||
self.assertEqual(rounded_total, 840)
|
self.assertEqual(rounded_total, 840)
|
||||||
frappe.set_user("Administrator")
|
|
||||||
|
|
||||||
def test_merging_with_validate_selling_price(self):
|
def test_merging_with_validate_selling_price(self):
|
||||||
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
|
||||||
@ -421,10 +431,12 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||||
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
|
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 1)
|
||||||
|
|
||||||
make_purchase_receipt(item_code="_Test Item", warehouse="_Test Warehouse - _TC", qty=1, rate=300)
|
item = "Test Selling Price Validation"
|
||||||
|
make_item(item, {"is_stock_item": 1})
|
||||||
|
make_purchase_receipt(item_code=item, warehouse="_Test Warehouse - _TC", qty=1, rate=300)
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
test_user, pos_profile = init_user_and_profile()
|
test_user, pos_profile = init_user_and_profile()
|
||||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
pos_inv = create_pos_invoice(item=item, rate=300, do_not_submit=1)
|
||||||
pos_inv.append('payments', {
|
pos_inv.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
})
|
})
|
||||||
@ -438,7 +450,7 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
})
|
})
|
||||||
self.assertRaises(frappe.ValidationError, pos_inv.submit)
|
self.assertRaises(frappe.ValidationError, pos_inv.submit)
|
||||||
|
|
||||||
pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
|
pos_inv2 = create_pos_invoice(item=item, rate=400, do_not_submit=1)
|
||||||
pos_inv2.append('payments', {
|
pos_inv2.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
|
||||||
})
|
})
|
||||||
@ -457,8 +469,6 @@ class TestPOSInvoice(unittest.TestCase):
|
|||||||
pos_inv2.load_from_db()
|
pos_inv2.load_from_db()
|
||||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||||
self.assertEqual(rounded_total, 400)
|
self.assertEqual(rounded_total, 400)
|
||||||
frappe.set_user("Administrator")
|
|
||||||
frappe.db.set_value("Selling Settings", "Selling Settings", "validate_selling_price", 0)
|
|
||||||
|
|
||||||
def create_pos_invoice(**args):
|
def create_pos_invoice(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@ -508,4 +518,4 @@ def create_pos_invoice(**args):
|
|||||||
else:
|
else:
|
||||||
pos_inv.payment_schedule = []
|
pos_inv.payment_schedule = []
|
||||||
|
|
||||||
return pos_inv
|
return pos_inv
|
||||||
|
@ -14,85 +14,89 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
|||||||
def test_consolidated_invoice_creation(self):
|
def test_consolidated_invoice_creation(self):
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
test_user, pos_profile = init_user_and_profile()
|
try:
|
||||||
|
test_user, pos_profile = init_user_and_profile()
|
||||||
|
|
||||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||||
pos_inv.append('payments', {
|
pos_inv.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
})
|
})
|
||||||
pos_inv.submit()
|
pos_inv.submit()
|
||||||
|
|
||||||
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||||
pos_inv2.append('payments', {
|
pos_inv2.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
||||||
})
|
})
|
||||||
pos_inv2.submit()
|
pos_inv2.submit()
|
||||||
|
|
||||||
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
||||||
pos_inv3.append('payments', {
|
pos_inv3.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
|
||||||
})
|
})
|
||||||
pos_inv3.submit()
|
pos_inv3.submit()
|
||||||
|
|
||||||
consolidate_pos_invoices()
|
consolidate_pos_invoices()
|
||||||
|
|
||||||
pos_inv.load_from_db()
|
pos_inv.load_from_db()
|
||||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
||||||
|
|
||||||
pos_inv3.load_from_db()
|
pos_inv3.load_from_db()
|
||||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv3.consolidated_invoice))
|
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv3.consolidated_invoice))
|
||||||
|
|
||||||
self.assertFalse(pos_inv.consolidated_invoice == pos_inv3.consolidated_invoice)
|
self.assertFalse(pos_inv.consolidated_invoice == pos_inv3.consolidated_invoice)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
frappe.set_user("Administrator")
|
||||||
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
|
||||||
|
|
||||||
def test_consolidated_credit_note_creation(self):
|
def test_consolidated_credit_note_creation(self):
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
test_user, pos_profile = init_user_and_profile()
|
try:
|
||||||
|
test_user, pos_profile = init_user_and_profile()
|
||||||
|
|
||||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||||
pos_inv.append('payments', {
|
pos_inv.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||||
})
|
})
|
||||||
pos_inv.submit()
|
pos_inv.submit()
|
||||||
|
|
||||||
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
|
||||||
pos_inv2.append('payments', {
|
pos_inv2.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200
|
||||||
})
|
})
|
||||||
pos_inv2.submit()
|
pos_inv2.submit()
|
||||||
|
|
||||||
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
|
||||||
pos_inv3.append('payments', {
|
pos_inv3.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 2300
|
||||||
})
|
})
|
||||||
pos_inv3.submit()
|
pos_inv3.submit()
|
||||||
|
|
||||||
pos_inv_cn = make_sales_return(pos_inv.name)
|
pos_inv_cn = make_sales_return(pos_inv.name)
|
||||||
pos_inv_cn.set("payments", [])
|
pos_inv_cn.set("payments", [])
|
||||||
pos_inv_cn.append('payments', {
|
pos_inv_cn.append('payments', {
|
||||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': -300
|
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': -300
|
||||||
})
|
})
|
||||||
pos_inv_cn.paid_amount = -300
|
pos_inv_cn.paid_amount = -300
|
||||||
pos_inv_cn.submit()
|
pos_inv_cn.submit()
|
||||||
|
|
||||||
consolidate_pos_invoices()
|
consolidate_pos_invoices()
|
||||||
|
|
||||||
pos_inv.load_from_db()
|
pos_inv.load_from_db()
|
||||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv.consolidated_invoice))
|
||||||
|
|
||||||
pos_inv3.load_from_db()
|
pos_inv3.load_from_db()
|
||||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv3.consolidated_invoice))
|
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv3.consolidated_invoice))
|
||||||
|
|
||||||
pos_inv_cn.load_from_db()
|
pos_inv_cn.load_from_db()
|
||||||
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv_cn.consolidated_invoice))
|
self.assertTrue(frappe.db.exists("Sales Invoice", pos_inv_cn.consolidated_invoice))
|
||||||
self.assertTrue(frappe.db.get_value("Sales Invoice", pos_inv_cn.consolidated_invoice, "is_return"))
|
self.assertTrue(frappe.db.get_value("Sales Invoice", pos_inv_cn.consolidated_invoice, "is_return"))
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
finally:
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.set_user("Administrator")
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,15 +20,16 @@ class POSOpeningEntry(StatusUpdater):
|
|||||||
|
|
||||||
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
if not cint(frappe.db.get_value("User", self.user, "enabled")):
|
||||||
frappe.throw(_("User {} is disabled. Please select valid user/cashier").format(self.user))
|
frappe.throw(_("User {} is disabled. Please select valid user/cashier").format(self.user))
|
||||||
|
|
||||||
def validate_payment_method_account(self):
|
def validate_payment_method_account(self):
|
||||||
invalid_modes = []
|
invalid_modes = []
|
||||||
for d in self.balance_details:
|
for d in self.balance_details:
|
||||||
account = frappe.db.get_value("Mode of Payment Account",
|
if d.mode_of_payment:
|
||||||
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
account = frappe.db.get_value("Mode of Payment Account",
|
||||||
if not account:
|
{"parent": d.mode_of_payment, "company": self.company}, "default_account")
|
||||||
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
if not account:
|
||||||
|
invalid_modes.append(get_link_to_form("Mode of Payment", d.mode_of_payment))
|
||||||
|
|
||||||
if invalid_modes:
|
if invalid_modes:
|
||||||
if invalid_modes == 1:
|
if invalid_modes == 1:
|
||||||
msg = _("Please set default Cash or Bank account in Mode of Payment {}")
|
msg = _("Please set default Cash or Bank account in Mode of Payment {}")
|
||||||
|
@ -44,6 +44,14 @@
|
|||||||
"column_break_21",
|
"column_break_21",
|
||||||
"min_amt",
|
"min_amt",
|
||||||
"max_amt",
|
"max_amt",
|
||||||
|
"product_discount_scheme_section",
|
||||||
|
"same_item",
|
||||||
|
"free_item",
|
||||||
|
"free_qty",
|
||||||
|
"free_item_rate",
|
||||||
|
"column_break_42",
|
||||||
|
"free_item_uom",
|
||||||
|
"is_recursive",
|
||||||
"section_break_23",
|
"section_break_23",
|
||||||
"valid_from",
|
"valid_from",
|
||||||
"valid_upto",
|
"valid_upto",
|
||||||
@ -62,13 +70,6 @@
|
|||||||
"discount_amount",
|
"discount_amount",
|
||||||
"discount_percentage",
|
"discount_percentage",
|
||||||
"for_price_list",
|
"for_price_list",
|
||||||
"product_discount_scheme_section",
|
|
||||||
"same_item",
|
|
||||||
"free_item",
|
|
||||||
"free_qty",
|
|
||||||
"column_break_51",
|
|
||||||
"free_item_uom",
|
|
||||||
"free_item_rate",
|
|
||||||
"section_break_13",
|
"section_break_13",
|
||||||
"threshold_percentage",
|
"threshold_percentage",
|
||||||
"priority",
|
"priority",
|
||||||
@ -357,7 +358,6 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.selling == 1",
|
|
||||||
"fieldname": "margin",
|
"fieldname": "margin",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Margin"
|
"label": "Margin"
|
||||||
@ -459,10 +459,6 @@
|
|||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"label": "Qty"
|
"label": "Qty"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "column_break_51",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "free_item_uom",
|
"fieldname": "free_item_uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -553,19 +549,33 @@
|
|||||||
"fieldname": "promotional_scheme",
|
"fieldname": "promotional_scheme",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Promotional Scheme",
|
"label": "Promotional Scheme",
|
||||||
"options": "Promotional Scheme"
|
"no_copy": 1,
|
||||||
|
"options": "Promotional Scheme",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Simple Python Expression, Example: territory != 'All Territories'",
|
"description": "Simple Python Expression, Example: territory != 'All Territories'",
|
||||||
"fieldname": "condition",
|
"fieldname": "condition",
|
||||||
"fieldtype": "Code",
|
"fieldtype": "Code",
|
||||||
"label": "Condition"
|
"label": "Condition"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_42",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",
|
||||||
|
"fieldname": "is_recursive",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Recursive"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-12-04 00:36:24.698219",
|
"modified": "2021-03-06 22:01:24.840422",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
@ -237,6 +237,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
|
|||||||
"doctype": args.doctype,
|
"doctype": args.doctype,
|
||||||
"has_margin": False,
|
"has_margin": False,
|
||||||
"name": args.name,
|
"name": args.name,
|
||||||
|
"free_item_data": [],
|
||||||
"parent": args.parent,
|
"parent": args.parent,
|
||||||
"parenttype": args.parenttype,
|
"parenttype": args.parenttype,
|
||||||
"child_docname": args.get('child_docname')
|
"child_docname": args.get('child_docname')
|
||||||
|
@ -367,7 +367,7 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args):
|
|||||||
|
|
||||||
if items and doc.get("items"):
|
if items and doc.get("items"):
|
||||||
for row in doc.get('items'):
|
for row in doc.get('items'):
|
||||||
if row.get(apply_on) not in items: continue
|
if (row.get(apply_on) or args.get(apply_on)) not in items: continue
|
||||||
|
|
||||||
if pr_doc.mixed_conditions:
|
if pr_doc.mixed_conditions:
|
||||||
amt = args.get('qty') * args.get("price_list_rate")
|
amt = args.get('qty') * args.get("price_list_rate")
|
||||||
@ -479,7 +479,7 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
|
|
||||||
doc.calculate_taxes_and_totals()
|
doc.calculate_taxes_and_totals()
|
||||||
elif d.price_or_product_discount == 'Product':
|
elif d.price_or_product_discount == 'Product':
|
||||||
item_details = frappe._dict({'parenttype': doc.doctype})
|
item_details = frappe._dict({'parenttype': doc.doctype, 'free_item_data': []})
|
||||||
get_product_discount_rule(d, item_details, doc=doc)
|
get_product_discount_rule(d, item_details, doc=doc)
|
||||||
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
apply_pricing_rule_for_free_items(doc, item_details.free_item_data)
|
||||||
doc.set_missing_values()
|
doc.set_missing_values()
|
||||||
@ -508,9 +508,16 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
|||||||
frappe.throw(_("Free item not set in the pricing rule {0}")
|
frappe.throw(_("Free item not set in the pricing rule {0}")
|
||||||
.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
|
.format(get_link_to_form("Pricing Rule", pricing_rule.name)))
|
||||||
|
|
||||||
item_details.free_item_data = {
|
qty = pricing_rule.free_qty or 1
|
||||||
|
if pricing_rule.is_recursive:
|
||||||
|
transaction_qty = args.get('qty') if args else doc.total_qty
|
||||||
|
if transaction_qty:
|
||||||
|
qty = flt(transaction_qty) * qty
|
||||||
|
|
||||||
|
free_item_data_args = {
|
||||||
'item_code': free_item,
|
'item_code': free_item,
|
||||||
'qty': pricing_rule.free_qty or 1,
|
'qty': qty,
|
||||||
|
'pricing_rules': pricing_rule.name,
|
||||||
'rate': pricing_rule.free_item_rate or 0,
|
'rate': pricing_rule.free_item_rate or 0,
|
||||||
'price_list_rate': pricing_rule.free_item_rate or 0,
|
'price_list_rate': pricing_rule.free_item_rate or 0,
|
||||||
'is_free_item': 1
|
'is_free_item': 1
|
||||||
@ -519,24 +526,26 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
|
|||||||
item_data = frappe.get_cached_value('Item', free_item, ['item_name',
|
item_data = frappe.get_cached_value('Item', free_item, ['item_name',
|
||||||
'description', 'stock_uom'], as_dict=1)
|
'description', 'stock_uom'], as_dict=1)
|
||||||
|
|
||||||
item_details.free_item_data.update(item_data)
|
free_item_data_args.update(item_data)
|
||||||
item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
|
free_item_data_args['uom'] = pricing_rule.free_item_uom or item_data.stock_uom
|
||||||
item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item,
|
free_item_data_args['conversion_factor'] = get_conversion_factor(free_item,
|
||||||
item_details.free_item_data['uom']).get("conversion_factor", 1)
|
free_item_data_args['uom']).get("conversion_factor", 1)
|
||||||
|
|
||||||
if item_details.get("parenttype") == 'Purchase Order':
|
if item_details.get("parenttype") == 'Purchase Order':
|
||||||
item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today()
|
free_item_data_args['schedule_date'] = doc.schedule_date if doc else today()
|
||||||
|
|
||||||
if item_details.get("parenttype") == 'Sales Order':
|
if item_details.get("parenttype") == 'Sales Order':
|
||||||
item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today()
|
free_item_data_args['delivery_date'] = doc.delivery_date if doc else today()
|
||||||
|
|
||||||
|
item_details.free_item_data.append(free_item_data_args)
|
||||||
|
|
||||||
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
|
||||||
if pricing_rule_args.get('item_code'):
|
if pricing_rule_args:
|
||||||
items = [d.item_code for d in doc.items
|
items = tuple([(d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item])
|
||||||
if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item]
|
|
||||||
|
|
||||||
if not items:
|
for args in pricing_rule_args:
|
||||||
doc.append('items', pricing_rule_args)
|
if not items or (args.get('item_code'), args.get('pricing_rules')) not in items:
|
||||||
|
doc.append('items', args)
|
||||||
|
|
||||||
def get_pricing_rule_items(pr_doc):
|
def get_pricing_rule_items(pr_doc):
|
||||||
apply_on_data = []
|
apply_on_data = []
|
||||||
|
@ -12,16 +12,16 @@ from frappe.model.document import Document
|
|||||||
pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group'
|
pricing_rule_fields = ['apply_on', 'mixed_conditions', 'is_cumulative', 'other_item_code', 'other_item_group'
|
||||||
'apply_rule_on_other', 'other_brand', 'selling', 'buying', 'applicable_for', 'valid_from',
|
'apply_rule_on_other', 'other_brand', 'selling', 'buying', 'applicable_for', 'valid_from',
|
||||||
'valid_upto', 'customer', 'customer_group', 'territory', 'sales_partner', 'campaign', 'supplier',
|
'valid_upto', 'customer', 'customer_group', 'territory', 'sales_partner', 'campaign', 'supplier',
|
||||||
'supplier_group', 'company', 'currency']
|
'supplier_group', 'company', 'currency', 'apply_multiple_pricing_rules']
|
||||||
|
|
||||||
other_fields = ['min_qty', 'max_qty', 'min_amt',
|
other_fields = ['min_qty', 'max_qty', 'min_amt',
|
||||||
'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description']
|
'max_amt', 'priority','warehouse', 'threshold_percentage', 'rule_description']
|
||||||
|
|
||||||
price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discount_on_rate',
|
price_discount_fields = ['rate_or_discount', 'apply_discount_on', 'apply_discount_on_rate',
|
||||||
'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule']
|
'rate', 'discount_amount', 'discount_percentage', 'validate_applied_rule', 'apply_multiple_pricing_rules']
|
||||||
|
|
||||||
product_discount_fields = ['free_item', 'free_qty', 'free_item_uom',
|
product_discount_fields = ['free_item', 'free_qty', 'free_item_uom',
|
||||||
'free_item_rate', 'same_item']
|
'free_item_rate', 'same_item', 'is_recursive', 'apply_multiple_pricing_rules']
|
||||||
|
|
||||||
class PromotionalScheme(Document):
|
class PromotionalScheme(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
@ -1,792 +1,181 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2019-03-24 14:48:59.649168",
|
"creation": "2019-03-24 14:48:59.649168",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"disable",
|
||||||
|
"apply_multiple_pricing_rules",
|
||||||
|
"column_break_2",
|
||||||
|
"rule_description",
|
||||||
|
"section_break_2",
|
||||||
|
"min_qty",
|
||||||
|
"max_qty",
|
||||||
|
"column_break_3",
|
||||||
|
"min_amount",
|
||||||
|
"max_amount",
|
||||||
|
"section_break_6",
|
||||||
|
"rate_or_discount",
|
||||||
|
"column_break_10",
|
||||||
|
"rate",
|
||||||
|
"discount_amount",
|
||||||
|
"discount_percentage",
|
||||||
|
"section_break_11",
|
||||||
|
"warehouse",
|
||||||
|
"threshold_percentage",
|
||||||
|
"validate_applied_rule",
|
||||||
|
"column_break_14",
|
||||||
|
"priority",
|
||||||
|
"apply_discount_on_rate"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
"default": "0",
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "disable",
|
"fieldname": "disable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Disable"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Disable",
|
|
||||||
"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,
|
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"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,
|
|
||||||
"fieldname": "rule_description",
|
"fieldname": "rule_description",
|
||||||
"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": "Rule Description",
|
"label": "Rule Description",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"fieldname": "section_break_2",
|
"fieldname": "section_break_2",
|
||||||
"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,
|
|
||||||
"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": 1,
|
"columns": 1,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "min_qty",
|
"fieldname": "min_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": "Min Qty"
|
||||||
"label": "Min Qty",
|
|
||||||
"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": 1,
|
"columns": 1,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "max_qty",
|
"fieldname": "max_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": "Max Qty"
|
||||||
"label": "Max Qty",
|
|
||||||
"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,
|
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"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,
|
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "min_amount",
|
"fieldname": "min_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": "Min Amount"
|
||||||
"label": "Min 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": 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",
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "max_amount",
|
"fieldname": "max_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": "Max Amount"
|
||||||
"label": "Max 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": 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": "",
|
|
||||||
"fieldname": "section_break_6",
|
"fieldname": "section_break_6",
|
||||||
"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,
|
|
||||||
"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": "Discount Percentage",
|
"default": "Discount Percentage",
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "rate_or_discount",
|
"fieldname": "rate_or_discount",
|
||||||
"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": "Discount Type",
|
"label": "Discount Type",
|
||||||
"length": 0,
|
"options": "\nRate\nDiscount Percentage\nDiscount Amount"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\nRate\nDiscount Percentage\nDiscount Amount",
|
|
||||||
"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,
|
|
||||||
"depends_on": "",
|
|
||||||
"fieldname": "column_break_10",
|
"fieldname": "column_break_10",
|
||||||
"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": 2,
|
"columns": 2,
|
||||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||||
"fieldname": "rate",
|
"fieldname": "rate",
|
||||||
"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": "Rate"
|
||||||
"label": "Rate",
|
|
||||||
"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,
|
|
||||||
"depends_on": "eval:doc.rate_or_discount==\"Discount Amount\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Discount Amount\"",
|
||||||
"fieldname": "discount_amount",
|
"fieldname": "discount_amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
"label": "Discount Amount"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Discount 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": 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": "eval:doc.rate_or_discount==\"Discount Percentage\"",
|
"depends_on": "eval:doc.rate_or_discount==\"Discount Percentage\"",
|
||||||
"fieldname": "discount_percentage",
|
"fieldname": "discount_percentage",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
"hidden": 0,
|
"label": "Discount Percentage"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Discount Percentage",
|
|
||||||
"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,
|
|
||||||
"fieldname": "section_break_11",
|
"fieldname": "section_break_11",
|
||||||
"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,
|
|
||||||
"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,
|
|
||||||
"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": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Warehouse",
|
"label": "Warehouse",
|
||||||
"length": 0,
|
"options": "Warehouse"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Warehouse",
|
|
||||||
"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,
|
|
||||||
"fieldname": "threshold_percentage",
|
"fieldname": "threshold_percentage",
|
||||||
"fieldtype": "Percent",
|
"fieldtype": "Percent",
|
||||||
"hidden": 0,
|
"label": "Threshold for Suggestion"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Threshold for Suggestion",
|
|
||||||
"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": "1",
|
"default": "1",
|
||||||
"fieldname": "validate_applied_rule",
|
"fieldname": "validate_applied_rule",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Validate Applied Rule"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Validate Applied Rule",
|
|
||||||
"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,
|
|
||||||
"fieldname": "column_break_14",
|
"fieldname": "column_break_14",
|
||||||
"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,
|
|
||||||
"fieldname": "priority",
|
"fieldname": "priority",
|
||||||
"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": "Priority",
|
"label": "Priority",
|
||||||
"length": 0,
|
"options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20",
|
|
||||||
"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": "priority",
|
"depends_on": "priority",
|
||||||
"fieldname": "apply_multiple_pricing_rules",
|
"fieldname": "apply_multiple_pricing_rules",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Apply Multiple Pricing Rules"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Apply Multiple Pricing Rules",
|
|
||||||
"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",
|
||||||
"depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
|
"depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
|
||||||
"fieldname": "apply_discount_on_rate",
|
"fieldname": "apply_discount_on_rate",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Apply Discount on Rate"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Apply Discount on Rate",
|
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"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-03-24 14:48:59.649168",
|
"modified": "2021-03-07 11:56:23.424137",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Promotional Scheme Price Discount",
|
"name": "Promotional Scheme Price Discount",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 0,
|
|
||||||
"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
|
|
||||||
}
|
}
|
@ -1,10 +1,12 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2019-03-24 14:48:59.649168",
|
"creation": "2019-03-24 14:48:59.649168",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"disable",
|
"disable",
|
||||||
|
"apply_multiple_pricing_rules",
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"rule_description",
|
"rule_description",
|
||||||
"section_break_1",
|
"section_break_1",
|
||||||
@ -25,7 +27,7 @@
|
|||||||
"threshold_percentage",
|
"threshold_percentage",
|
||||||
"column_break_15",
|
"column_break_15",
|
||||||
"priority",
|
"priority",
|
||||||
"apply_multiple_pricing_rules"
|
"is_recursive"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -152,10 +154,19 @@
|
|||||||
"fieldname": "apply_multiple_pricing_rules",
|
"fieldname": "apply_multiple_pricing_rules",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Apply Multiple Pricing Rules"
|
"label": "Apply Multiple Pricing Rules"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"description": "Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on",
|
||||||
|
"fieldname": "is_recursive",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Is Recursive"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-21 00:00:56.674284",
|
"links": [],
|
||||||
|
"modified": "2021-03-06 21:58:18.162346",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Promotional Scheme Product Discount",
|
"name": "Promotional Scheme Product Discount",
|
||||||
|
@ -524,7 +524,7 @@ frappe.ui.form.on("Purchase Invoice", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
if(frm.doc.__onload) {
|
if(frm.doc.__onload && frm.is_new()) {
|
||||||
if(frm.doc.supplier) {
|
if(frm.doc.supplier) {
|
||||||
frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
|
frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
@ -898,7 +898,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
acc_settings.submit_journal_entries = 1
|
acc_settings.submit_journal_entries = 1
|
||||||
acc_settings.save()
|
acc_settings.save()
|
||||||
|
|
||||||
item = create_item("_Test Item for Deferred Accounting")
|
item = create_item("_Test Item for Deferred Accounting", is_purchase_item=True)
|
||||||
item.enable_deferred_expense = 1
|
item.enable_deferred_expense = 1
|
||||||
item.deferred_expense_account = deferred_account
|
item.deferred_expense_account = deferred_account
|
||||||
item.save()
|
item.save()
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grand_total": 0,
|
"grand_total": 0,
|
||||||
"naming_series": "_T-BILL",
|
"naming_series": "T-PINV-",
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
"account_head": "_Test Account Shipping Charges - _TC",
|
"account_head": "_Test Account Shipping Charges - _TC",
|
||||||
@ -167,7 +167,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grand_total": 0,
|
"grand_total": 0,
|
||||||
"naming_series": "_T-Purchase Invoice-",
|
"naming_series": "T-PINV-",
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
"account_head": "_Test Account Shipping Charges - _TC",
|
"account_head": "_Test Account Shipping Charges - _TC",
|
||||||
|
@ -28,10 +28,16 @@
|
|||||||
"stock_qty",
|
"stock_qty",
|
||||||
"sec_break1",
|
"sec_break1",
|
||||||
"price_list_rate",
|
"price_list_rate",
|
||||||
"discount_percentage",
|
|
||||||
"discount_amount",
|
|
||||||
"col_break3",
|
"col_break3",
|
||||||
"base_price_list_rate",
|
"base_price_list_rate",
|
||||||
|
"section_break_26",
|
||||||
|
"margin_type",
|
||||||
|
"margin_rate_or_amount",
|
||||||
|
"rate_with_margin",
|
||||||
|
"column_break_30",
|
||||||
|
"discount_percentage",
|
||||||
|
"discount_amount",
|
||||||
|
"base_rate_with_margin",
|
||||||
"sec_break2",
|
"sec_break2",
|
||||||
"rate",
|
"rate",
|
||||||
"amount",
|
"amount",
|
||||||
@ -789,6 +795,7 @@
|
|||||||
"fieldname": "stock_uom_rate",
|
"fieldname": "stock_uom_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate of Stock UOM",
|
"label": "Rate of Stock UOM",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@ -799,12 +806,54 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_26",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Discount and Margin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "price_list_rate",
|
||||||
|
"fieldname": "margin_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Margin Type",
|
||||||
|
"options": "\nPercentage\nAmount",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate",
|
||||||
|
"fieldname": "margin_rate_or_amount",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Margin Rate or Amount",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||||
|
"fieldname": "rate_with_margin",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Rate With Margin",
|
||||||
|
"options": "currency",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_30",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||||
|
"fieldname": "base_rate_with_margin",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Rate With Margin (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-30 21:43:21.488258",
|
"modified": "2021-02-23 00:59:52.614805",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
@ -1952,13 +1952,12 @@
|
|||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"custom": 1,
|
|
||||||
"group": "Reference",
|
"group": "Reference",
|
||||||
"link_doctype": "POS Invoice",
|
"link_doctype": "POS Invoice",
|
||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-02-01 15:42:26.261540",
|
"modified": "2021-03-31 15:42:26.261540",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
@ -76,7 +76,7 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
if not self.is_pos:
|
if not self.is_pos:
|
||||||
self.so_dn_required()
|
self.so_dn_required()
|
||||||
|
|
||||||
self.set_tax_withholding()
|
self.set_tax_withholding()
|
||||||
|
|
||||||
self.validate_proj_cust()
|
self.validate_proj_cust()
|
||||||
@ -390,6 +390,7 @@ class SalesInvoice(SellingController):
|
|||||||
if validate_against_credit_limit:
|
if validate_against_credit_limit:
|
||||||
check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
|
check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate=False):
|
||||||
pos = self.set_pos_fields(for_validate)
|
pos = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
@ -729,6 +730,7 @@ class SalesInvoice(SellingController):
|
|||||||
else:
|
else:
|
||||||
self.calculate_billing_amount_for_timesheet()
|
self.calculate_billing_amount_for_timesheet()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def add_timesheet_data(self):
|
def add_timesheet_data(self):
|
||||||
self.set('timesheets', [])
|
self.set('timesheets', [])
|
||||||
if self.project:
|
if self.project:
|
||||||
@ -1286,6 +1288,7 @@ class SalesInvoice(SellingController):
|
|||||||
break
|
break
|
||||||
|
|
||||||
# Healthcare
|
# Healthcare
|
||||||
|
@frappe.whitelist()
|
||||||
def set_healthcare_services(self, checked_values):
|
def set_healthcare_services(self, checked_values):
|
||||||
self.set("items", [])
|
self.set("items", [])
|
||||||
from erpnext.stock.get_item_details import get_item_details
|
from erpnext.stock.get_item_details import get_item_details
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
"base_grand_total": 561.8,
|
"base_grand_total": 561.8,
|
||||||
"grand_total": 561.8,
|
"grand_total": 561.8,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"naming_series": "_T-Sales Invoice-",
|
"naming_series": "T-SINV-",
|
||||||
"base_net_total": 500.0,
|
"base_net_total": 500.0,
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
@ -104,7 +104,7 @@
|
|||||||
"base_grand_total": 630.0,
|
"base_grand_total": 630.0,
|
||||||
"grand_total": 630.0,
|
"grand_total": 630.0,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"naming_series": "_T-Sales Invoice-",
|
"naming_series": "T-SINV-",
|
||||||
"base_net_total": 500.0,
|
"base_net_total": 500.0,
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
@ -175,7 +175,7 @@
|
|||||||
],
|
],
|
||||||
"grand_total": 0,
|
"grand_total": 0,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"naming_series": "_T-Sales Invoice-",
|
"naming_series": "T-SINV-",
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
"account_head": "_Test Account Shipping Charges - _TC",
|
"account_head": "_Test Account Shipping Charges - _TC",
|
||||||
@ -301,7 +301,7 @@
|
|||||||
],
|
],
|
||||||
"grand_total": 0,
|
"grand_total": 0,
|
||||||
"is_pos": 0,
|
"is_pos": 0,
|
||||||
"naming_series": "_T-Sales Invoice-",
|
"naming_series": "T-SINV-",
|
||||||
"taxes": [
|
"taxes": [
|
||||||
{
|
{
|
||||||
"account_head": "_Test Account Excise Duty - _TC",
|
"account_head": "_Test Account Excise Duty - _TC",
|
||||||
|
@ -1800,6 +1800,15 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
si.selling_price_list = "_Test Price List Rest of the World"
|
si.selling_price_list = "_Test Price List Rest of the World"
|
||||||
si.update_stock = 1
|
si.update_stock = 1
|
||||||
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||||
|
|
||||||
|
# Add stock to stores for succesful stock transfer
|
||||||
|
make_stock_entry(
|
||||||
|
target="Stores - TCP1",
|
||||||
|
company = "_Test Company with perpetual inventory",
|
||||||
|
qty=1,
|
||||||
|
basic_rate=100
|
||||||
|
)
|
||||||
|
|
||||||
add_taxes(si)
|
add_taxes(si)
|
||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
@ -2106,6 +2115,7 @@ def create_sales_invoice(**args):
|
|||||||
si.return_against = args.return_against
|
si.return_against = args.return_against
|
||||||
si.currency=args.currency or "INR"
|
si.currency=args.currency or "INR"
|
||||||
si.conversion_rate = args.conversion_rate or 1
|
si.conversion_rate = args.conversion_rate or 1
|
||||||
|
si.naming_series = args.naming_series or "T-SINV-"
|
||||||
|
|
||||||
si.append("items", {
|
si.append("items", {
|
||||||
"item_code": args.item or args.item_code or "_Test Item",
|
"item_code": args.item or args.item_code or "_Test Item",
|
||||||
@ -2269,4 +2279,4 @@ def add_taxes(doc):
|
|||||||
"cost_center": "Main - TCP1",
|
"cost_center": "Main - TCP1",
|
||||||
"description": "Excise Duty",
|
"description": "Excise Duty",
|
||||||
"rate": 12
|
"rate": 12
|
||||||
})
|
})
|
||||||
|
@ -818,6 +818,7 @@
|
|||||||
"fieldname": "stock_uom_rate",
|
"fieldname": "stock_uom_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate of Stock UOM",
|
"label": "Rate of Stock UOM",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
@ -825,7 +826,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-30 21:42:37.796771",
|
"modified": "2021-02-23 01:05:22.123527",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
@ -46,5 +46,5 @@ def validate_disabled(doc):
|
|||||||
frappe.throw(_("Disabled template must not be default template"))
|
frappe.throw(_("Disabled template must not be default template"))
|
||||||
|
|
||||||
def validate_for_tax_category(doc):
|
def validate_for_tax_category(doc):
|
||||||
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
|
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0, "name": ["!=", doc.name]}):
|
||||||
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
|
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))
|
||||||
|
@ -14,10 +14,15 @@ test_records = frappe.get_test_records('Tax Rule')
|
|||||||
from six import iteritems
|
from six import iteritems
|
||||||
|
|
||||||
class TestTaxRule(unittest.TestCase):
|
class TestTaxRule(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
frappe.db.set_value("Shopping Cart Settings", None, "enabled", 0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
frappe.db.sql("delete from `tabTax Rule`")
|
frappe.db.sql("delete from `tabTax Rule`")
|
||||||
|
|
||||||
def tearDown(self):
|
def setUp(self):
|
||||||
frappe.db.sql("delete from `tabTax Rule`")
|
frappe.db.sql("delete from `tabTax Rule`")
|
||||||
|
|
||||||
def test_conflict(self):
|
def test_conflict(self):
|
||||||
|
@ -177,7 +177,7 @@ def cancel_invoices():
|
|||||||
|
|
||||||
for d in purchase_invoices:
|
for d in purchase_invoices:
|
||||||
frappe.get_doc('Purchase Invoice', d).cancel()
|
frappe.get_doc('Purchase Invoice', d).cancel()
|
||||||
|
|
||||||
for d in sales_invoices:
|
for d in sales_invoices:
|
||||||
frappe.get_doc('Sales Invoice', d).cancel()
|
frappe.get_doc('Sales Invoice', d).cancel()
|
||||||
|
|
||||||
@ -229,7 +229,8 @@ def create_sales_invoice(**args):
|
|||||||
'qty': args.qty or 1,
|
'qty': args.qty or 1,
|
||||||
'rate': args.rate or 10000,
|
'rate': args.rate or 10000,
|
||||||
'cost_center': 'Main - _TC',
|
'cost_center': 'Main - _TC',
|
||||||
'expense_account': 'Cost of Goods Sold - _TC'
|
'expense_account': 'Cost of Goods Sold - _TC',
|
||||||
|
'warehouse': args.warehouse or '_Test Warehouse - _TC'
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -353,4 +354,4 @@ def create_tax_with_holding_category():
|
|||||||
'company': '_Test Company',
|
'company': '_Test Company',
|
||||||
'account': 'TDS - _TC'
|
'account': 'TDS - _TC'
|
||||||
}]
|
}]
|
||||||
}).insert()
|
}).insert()
|
||||||
|
@ -364,7 +364,7 @@ class ReceivablePayableReport(object):
|
|||||||
payment_terms_details = frappe.db.sql("""
|
payment_terms_details = frappe.db.sql("""
|
||||||
select
|
select
|
||||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||||
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount
|
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
|
||||||
from `tab{0}` si, `tabPayment Schedule` ps
|
from `tab{0}` si, `tabPayment Schedule` ps
|
||||||
where
|
where
|
||||||
si.name = ps.parent and
|
si.name = ps.parent and
|
||||||
@ -395,13 +395,13 @@ class ReceivablePayableReport(object):
|
|||||||
"invoiced": invoiced,
|
"invoiced": invoiced,
|
||||||
"invoice_grand_total": row.invoiced,
|
"invoice_grand_total": row.invoiced,
|
||||||
"payment_term": d.description,
|
"payment_term": d.description,
|
||||||
"paid": d.paid_amount,
|
"paid": d.paid_amount + d.discounted_amount,
|
||||||
"credit_note": 0.0,
|
"credit_note": 0.0,
|
||||||
"outstanding": invoiced - d.paid_amount
|
"outstanding": invoiced - d.paid_amount - d.discounted_amount
|
||||||
}))
|
}))
|
||||||
|
|
||||||
if d.paid_amount:
|
if d.paid_amount:
|
||||||
row['paid'] -= d.paid_amount
|
row['paid'] -= d.paid_amount + d.discounted_amount
|
||||||
|
|
||||||
def allocate_closing_to_term(self, row, term, key):
|
def allocate_closing_to_term(self, row, term, key):
|
||||||
if row[key]:
|
if row[key]:
|
||||||
|
@ -51,7 +51,11 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
|
|||||||
"from_date": start_date
|
"from_date": start_date
|
||||||
})
|
})
|
||||||
|
|
||||||
to_date = add_months(start_date, months_to_add)
|
if i==0 and filter_based_on == 'Date Range':
|
||||||
|
to_date = add_months(get_first_day(start_date), months_to_add)
|
||||||
|
else:
|
||||||
|
to_date = add_months(start_date, months_to_add)
|
||||||
|
|
||||||
start_date = to_date
|
start_date = to_date
|
||||||
|
|
||||||
# Subtract one day from to_date, as it may be first day in next fiscal year or month
|
# Subtract one day from to_date, as it may be first day in next fiscal year or month
|
||||||
|
@ -71,6 +71,7 @@ class CropCycle(Document):
|
|||||||
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def reload_linked_analysis(self):
|
def reload_linked_analysis(self):
|
||||||
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
|
||||||
required_fields = ['location', 'name', 'collection_datetime']
|
required_fields = ['location', 'name', 'collection_datetime']
|
||||||
@ -87,6 +88,7 @@ class CropCycle(Document):
|
|||||||
frappe.publish_realtime("List of Linked Docs",
|
frappe.publish_realtime("List of Linked Docs",
|
||||||
output, user=frappe.session.user)
|
output, user=frappe.session.user)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def append_to_child(self, obj_to_append):
|
def append_to_child(self, obj_to_append):
|
||||||
for doctype in obj_to_append:
|
for doctype in obj_to_append:
|
||||||
for doc_name in set(obj_to_append[doctype]):
|
for doc_name in set(obj_to_append[doctype]):
|
||||||
|
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class Fertilizer(Document):
|
class Fertilizer(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
@ -8,6 +8,7 @@ from frappe.model.naming import make_autoname
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class PlantAnalysis(Document):
|
class PlantAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class SoilAnalysis(Document):
|
class SoilAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
@ -13,6 +13,7 @@ class SoilTexture(Document):
|
|||||||
soil_edit_order = [2, 1, 0]
|
soil_edit_order = [2, 1, 0]
|
||||||
soil_types = ['clay_composition', 'sand_composition', 'silt_composition']
|
soil_types = ['clay_composition', 'sand_composition', 'silt_composition']
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Texture'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Texture'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
@ -26,6 +27,7 @@ class SoilTexture(Document):
|
|||||||
if sum(self.get(soil_type) for soil_type in self.soil_types) != 100:
|
if sum(self.get(soil_type) for soil_type in self.soil_types) != 100:
|
||||||
frappe.throw(_('Soil compositions do not add up to 100'))
|
frappe.throw(_('Soil compositions do not add up to 100'))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_soil_edit(self, soil_type):
|
def update_soil_edit(self, soil_type):
|
||||||
self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1
|
self.soil_edit_order[self.soil_types.index(soil_type)] = max(self.soil_edit_order)+1
|
||||||
self.soil_type = self.get_soil_type()
|
self.soil_type = self.get_soil_type()
|
||||||
@ -35,8 +37,8 @@ class SoilTexture(Document):
|
|||||||
if sum(self.soil_edit_order) < 5: return
|
if sum(self.soil_edit_order) < 5: return
|
||||||
last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order))
|
last_edit_index = self.soil_edit_order.index(min(self.soil_edit_order))
|
||||||
|
|
||||||
# set composition of the last edited soil
|
# set composition of the last edited soil
|
||||||
self.set( self.soil_types[last_edit_index],
|
self.set(self.soil_types[last_edit_index],
|
||||||
100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index])))
|
100 - sum(cint(self.get(soil_type)) for soil_type in self.soil_types) + cint(self.get(self.soil_types[last_edit_index])))
|
||||||
|
|
||||||
# calculate soil type
|
# calculate soil type
|
||||||
@ -67,4 +69,4 @@ class SoilTexture(Document):
|
|||||||
elif (c >= 40 and sa <= 45 and si < 40):
|
elif (c >= 40 and sa <= 45 and si < 40):
|
||||||
return 'Clay'
|
return 'Clay'
|
||||||
else:
|
else:
|
||||||
return 'Select'
|
return 'Select'
|
||||||
|
@ -9,11 +9,13 @@ from frappe.model.document import Document
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
|
|
||||||
class WaterAnalysis(Document):
|
class WaterAnalysis(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Water Analysis'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Water Analysis'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
self.append('water_analysis_criteria', {'title': str(doc.name)})
|
self.append('water_analysis_criteria', {'title': str(doc.name)})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def update_lab_result_date(self):
|
def update_lab_result_date(self):
|
||||||
if not self.result_datetime:
|
if not self.result_datetime:
|
||||||
self.result_datetime = self.laboratory_testing_datetime
|
self.result_datetime = self.laboratory_testing_datetime
|
||||||
|
@ -7,6 +7,7 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class Weather(Document):
|
class Weather(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def load_contents(self):
|
def load_contents(self):
|
||||||
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Weather'})
|
docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Weather'})
|
||||||
for doc in docs:
|
for doc in docs:
|
||||||
|
@ -553,6 +553,7 @@ class Asset(AccountsController):
|
|||||||
make_gl_entries(gl_entries)
|
make_gl_entries(gl_entries)
|
||||||
self.db_set('booked_fixed_asset', 1)
|
self.db_set('booked_fixed_asset', 1)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_depreciation_rate(self, args, on_validate=False):
|
def get_depreciation_rate(self, args, on_validate=False):
|
||||||
if isinstance(args, string_types):
|
if isinstance(args, string_types):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
@ -133,6 +133,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
d.material_request_item, "schedule_date")
|
d.material_request_item, "schedule_date")
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_last_purchase_rate(self):
|
def get_last_purchase_rate(self):
|
||||||
"""get last purchase rates for all items"""
|
"""get last purchase rates for all items"""
|
||||||
|
|
||||||
@ -252,6 +253,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
self.update_prevdoc_status()
|
self.update_prevdoc_status()
|
||||||
|
|
||||||
# Must be called after updating ordered qty in Material Request
|
# Must be called after updating ordered qty in Material Request
|
||||||
|
# bin uses Material Request Items to recalculate & update
|
||||||
self.update_requested_qty()
|
self.update_requested_qty()
|
||||||
self.update_ordered_qty()
|
self.update_ordered_qty()
|
||||||
|
|
||||||
@ -366,7 +368,6 @@ def make_purchase_receipt(source_name, target_doc=None):
|
|||||||
"Purchase Order": {
|
"Purchase Order": {
|
||||||
"doctype": "Purchase Receipt",
|
"doctype": "Purchase Receipt",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"per_billed": "per_billed",
|
|
||||||
"supplier_warehouse":"supplier_warehouse"
|
"supplier_warehouse":"supplier_warehouse"
|
||||||
},
|
},
|
||||||
"validation": {
|
"validation": {
|
||||||
|
@ -90,6 +90,50 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0)
|
frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0)
|
||||||
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
|
||||||
|
|
||||||
|
def test_update_remove_child_linked_to_mr(self):
|
||||||
|
"""Test impact on linked PO and MR on deleting/updating row."""
|
||||||
|
mr = make_material_request(qty=10)
|
||||||
|
po = make_purchase_order(mr.name)
|
||||||
|
po.supplier = "_Test Supplier"
|
||||||
|
po.save()
|
||||||
|
po.submit()
|
||||||
|
|
||||||
|
first_item_of_po = po.get("items")[0]
|
||||||
|
existing_ordered_qty = get_ordered_qty() # 10
|
||||||
|
existing_requested_qty = get_requested_qty() # 0
|
||||||
|
|
||||||
|
# decrease ordered qty by 3 (10 -> 7) and add item
|
||||||
|
trans_item = json.dumps([
|
||||||
|
{
|
||||||
|
'item_code': first_item_of_po.item_code,
|
||||||
|
'rate': first_item_of_po.rate,
|
||||||
|
'qty': 7,
|
||||||
|
'docname': first_item_of_po.name
|
||||||
|
},
|
||||||
|
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
|
||||||
|
])
|
||||||
|
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||||
|
mr.reload()
|
||||||
|
|
||||||
|
# requested qty increases as ordered qty decreases
|
||||||
|
self.assertEqual(get_requested_qty(), existing_requested_qty + 3) # 3
|
||||||
|
self.assertEqual(mr.items[0].ordered_qty, 7)
|
||||||
|
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty - 3) # 7
|
||||||
|
|
||||||
|
# delete first item linked to Material Request
|
||||||
|
trans_item = json.dumps([
|
||||||
|
{'item_code' : '_Test Item 2', 'rate' : 200, 'qty' : 2}
|
||||||
|
])
|
||||||
|
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||||
|
mr.reload()
|
||||||
|
|
||||||
|
# requested qty increases as ordered qty is 0 (deleted row)
|
||||||
|
self.assertEqual(get_requested_qty(), existing_requested_qty + 10) # 10
|
||||||
|
self.assertEqual(mr.items[0].ordered_qty, 0)
|
||||||
|
|
||||||
|
# ordered qty decreases as ordered qty is 0 (deleted row)
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10) # 0
|
||||||
|
|
||||||
def test_update_child(self):
|
def test_update_child(self):
|
||||||
mr = make_material_request(qty=10)
|
mr = make_material_request(qty=10)
|
||||||
@ -120,7 +164,6 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||||
|
|
||||||
|
|
||||||
def test_update_child_adding_new_item(self):
|
def test_update_child_adding_new_item(self):
|
||||||
po = create_purchase_order(do_not_save=1)
|
po = create_purchase_order(do_not_save=1)
|
||||||
po.items[0].qty = 4
|
po.items[0].qty = 4
|
||||||
@ -129,6 +172,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
pr = make_pr_against_po(po.name, 2)
|
pr = make_pr_against_po(po.name, 2)
|
||||||
|
|
||||||
po.load_from_db()
|
po.load_from_db()
|
||||||
|
existing_ordered_qty = get_ordered_qty()
|
||||||
first_item_of_po = po.get("items")[0]
|
first_item_of_po = po.get("items")[0]
|
||||||
|
|
||||||
trans_item = json.dumps([
|
trans_item = json.dumps([
|
||||||
@ -145,7 +189,8 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
po.reload()
|
po.reload()
|
||||||
self.assertEquals(len(po.get('items')), 2)
|
self.assertEquals(len(po.get('items')), 2)
|
||||||
self.assertEqual(po.status, 'To Receive and Bill')
|
self.assertEqual(po.status, 'To Receive and Bill')
|
||||||
|
# ordered qty should increase on row addition
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
|
||||||
|
|
||||||
def test_update_child_removing_item(self):
|
def test_update_child_removing_item(self):
|
||||||
po = create_purchase_order(do_not_save=1)
|
po = create_purchase_order(do_not_save=1)
|
||||||
@ -156,6 +201,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
|
|
||||||
po.reload()
|
po.reload()
|
||||||
first_item_of_po = po.get("items")[0]
|
first_item_of_po = po.get("items")[0]
|
||||||
|
existing_ordered_qty = get_ordered_qty()
|
||||||
# add an item
|
# add an item
|
||||||
trans_item = json.dumps([
|
trans_item = json.dumps([
|
||||||
{
|
{
|
||||||
@ -168,6 +214,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
update_child_qty_rate('Purchase Order', trans_item, po.name)
|
||||||
|
|
||||||
po.reload()
|
po.reload()
|
||||||
|
|
||||||
|
# ordered qty should increase on row addition
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 7)
|
||||||
|
|
||||||
# check if can remove received item
|
# check if can remove received item
|
||||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
|
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}])
|
||||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
|
self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name)
|
||||||
@ -187,6 +237,9 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
self.assertEquals(len(po.get('items')), 1)
|
self.assertEquals(len(po.get('items')), 1)
|
||||||
self.assertEqual(po.status, 'To Receive and Bill')
|
self.assertEqual(po.status, 'To Receive and Bill')
|
||||||
|
|
||||||
|
# ordered qty should decrease (back to initial) on row deletion
|
||||||
|
self.assertEqual(get_ordered_qty(), existing_ordered_qty)
|
||||||
|
|
||||||
def test_update_child_perm(self):
|
def test_update_child_perm(self):
|
||||||
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
po = create_purchase_order(item_code= "_Test Item", qty=4)
|
||||||
|
|
||||||
@ -230,11 +283,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
|
|
||||||
new_item_with_tax = frappe.get_doc("Item", "Test Item with Tax")
|
new_item_with_tax = frappe.get_doc("Item", "Test Item with Tax")
|
||||||
|
|
||||||
new_item_with_tax.append("taxes", {
|
if not frappe.db.exists("Item Tax",
|
||||||
"item_tax_template": "Test Update Items Template - _TC",
|
{"item_tax_template": "Test Update Items Template - _TC", "parent": "Test Item with Tax"}):
|
||||||
"valid_from": nowdate()
|
new_item_with_tax.append("taxes", {
|
||||||
})
|
"item_tax_template": "Test Update Items Template - _TC",
|
||||||
new_item_with_tax.save()
|
"valid_from": nowdate()
|
||||||
|
})
|
||||||
|
new_item_with_tax.save()
|
||||||
|
|
||||||
tax_template = "_Test Account Excise Duty @ 10 - _TC"
|
tax_template = "_Test Account Excise Duty @ 10 - _TC"
|
||||||
item = "_Test Item Home Desktop 100"
|
item = "_Test Item Home Desktop 100"
|
||||||
@ -723,7 +778,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
|||||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
|
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
|
||||||
|
|
||||||
make_stock_entry(target="_Test Warehouse - _TC",
|
make_stock_entry(target="_Test Warehouse - _TC",
|
||||||
item_code="_Test Item Home Desktop 100", qty=10, basic_rate=100)
|
item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100)
|
||||||
make_stock_entry(target="_Test Warehouse - _TC",
|
make_stock_entry(target="_Test Warehouse - _TC",
|
||||||
item_code = "Test Extra Item 1", qty=100, basic_rate=100)
|
item_code = "Test Extra Item 1", qty=100, basic_rate=100)
|
||||||
make_stock_entry(target="_Test Warehouse - _TC",
|
make_stock_entry(target="_Test Warehouse - _TC",
|
||||||
|
@ -27,11 +27,17 @@
|
|||||||
"stock_qty",
|
"stock_qty",
|
||||||
"sec_break1",
|
"sec_break1",
|
||||||
"price_list_rate",
|
"price_list_rate",
|
||||||
|
"last_purchase_rate",
|
||||||
|
"col_break3",
|
||||||
|
"base_price_list_rate",
|
||||||
|
"discount_and_margin_section",
|
||||||
|
"margin_type",
|
||||||
|
"margin_rate_or_amount",
|
||||||
|
"rate_with_margin",
|
||||||
|
"column_break_28",
|
||||||
"discount_percentage",
|
"discount_percentage",
|
||||||
"discount_amount",
|
"discount_amount",
|
||||||
"col_break3",
|
"base_rate_with_margin",
|
||||||
"last_purchase_rate",
|
|
||||||
"base_price_list_rate",
|
|
||||||
"sec_break2",
|
"sec_break2",
|
||||||
"rate",
|
"rate",
|
||||||
"amount",
|
"amount",
|
||||||
@ -733,15 +739,59 @@
|
|||||||
"fieldname": "stock_uom_rate",
|
"fieldname": "stock_uom_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Rate of Stock UOM",
|
"label": "Rate of Stock UOM",
|
||||||
|
"no_copy": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "discount_and_margin_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Discount and Margin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "price_list_rate",
|
||||||
|
"fieldname": "margin_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"label": "Margin Type",
|
||||||
|
"options": "\nPercentage\nAmount",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate",
|
||||||
|
"fieldname": "margin_rate_or_amount",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Margin Rate or Amount",
|
||||||
|
"print_hide": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||||
|
"fieldname": "rate_with_margin",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Rate With Margin",
|
||||||
|
"options": "currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_28",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
|
||||||
|
"fieldname": "base_rate_with_margin",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": "Rate With Margin (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"print_hide": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-01-30 21:44:41.816974",
|
"modified": "2021-02-23 01:00:27.132705",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
@ -66,6 +66,7 @@ class RequestforQuotation(BuyingController):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
frappe.db.set(self, 'status', 'Cancelled')
|
frappe.db.set(self, 'status', 'Cancelled')
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_supplier_email_preview(self, supplier):
|
def get_supplier_email_preview(self, supplier):
|
||||||
"""Returns formatted email preview as string."""
|
"""Returns formatted email preview as string."""
|
||||||
rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers))
|
rfq_suppliers = list(filter(lambda row: row.supplier == supplier, self.suppliers))
|
||||||
|
@ -9,9 +9,7 @@ import unittest
|
|||||||
class TestSupplierScorecard(unittest.TestCase):
|
class TestSupplierScorecard(unittest.TestCase):
|
||||||
|
|
||||||
def test_create_scorecard(self):
|
def test_create_scorecard(self):
|
||||||
delete_test_scorecards()
|
doc = make_supplier_scorecard().insert()
|
||||||
my_doc = make_supplier_scorecard()
|
|
||||||
doc = my_doc.insert()
|
|
||||||
self.assertEqual(doc.name, valid_scorecard[0].get("supplier"))
|
self.assertEqual(doc.name, valid_scorecard[0].get("supplier"))
|
||||||
|
|
||||||
def test_criteria_weight(self):
|
def test_criteria_weight(self):
|
||||||
@ -121,7 +119,8 @@ valid_scorecard = [
|
|||||||
{
|
{
|
||||||
"weight":100.0,
|
"weight":100.0,
|
||||||
"doctype":"Supplier Scorecard Scoring Criteria",
|
"doctype":"Supplier Scorecard Scoring Criteria",
|
||||||
"criteria_name":"Delivery"
|
"criteria_name":"Delivery",
|
||||||
|
"formula": "100"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"supplier":"_Test Supplier",
|
"supplier":"_Test Supplier",
|
||||||
|
@ -26,7 +26,8 @@ from erpnext.controllers.print_settings import set_print_templates_for_item_tabl
|
|||||||
|
|
||||||
class AccountMissingError(frappe.ValidationError): pass
|
class AccountMissingError(frappe.ValidationError): pass
|
||||||
|
|
||||||
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
|
force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate",
|
||||||
|
"pricing_rules", "weight_per_unit", "weight_uom", "total_weight")
|
||||||
|
|
||||||
class AccountsController(TransactionBase):
|
class AccountsController(TransactionBase):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -516,6 +517,7 @@ class AccountsController(TransactionBase):
|
|||||||
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
frappe.db.sql("""delete from `tab%s` where parentfield=%s and parent = %s
|
||||||
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
and allocated_amount = 0""" % (childtype, '%s', '%s'), (parentfield, self.name))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def apply_shipping_rule(self):
|
def apply_shipping_rule(self):
|
||||||
if self.shipping_rule:
|
if self.shipping_rule:
|
||||||
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
|
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
|
||||||
@ -536,6 +538,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_advances(self):
|
def set_advances(self):
|
||||||
"""Returns list of advances against Account, Party, Reference"""
|
"""Returns list of advances against Account, Party, Reference"""
|
||||||
|
|
||||||
@ -920,7 +923,8 @@ class AccountsController(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
for d in self.get("payment_schedule"):
|
for d in self.get("payment_schedule"):
|
||||||
if d.invoice_portion:
|
if d.invoice_portion:
|
||||||
d.payment_amount = flt(grand_total * flt(d.invoice_portion) / 100, d.precision('payment_amount'))
|
d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount'))
|
||||||
|
d.outstanding = d.payment_amount
|
||||||
|
|
||||||
def set_due_date(self):
|
def set_due_date(self):
|
||||||
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
|
||||||
@ -1235,18 +1239,24 @@ def get_payment_term_details(term, posting_date=None, grand_total=None, bill_dat
|
|||||||
term_details.description = term.description
|
term_details.description = term.description
|
||||||
term_details.invoice_portion = term.invoice_portion
|
term_details.invoice_portion = term.invoice_portion
|
||||||
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
term_details.payment_amount = flt(term.invoice_portion) * flt(grand_total) / 100
|
||||||
|
term_details.discount_type = term.discount_type
|
||||||
|
term_details.discount = term.discount
|
||||||
|
# term_details.discounted_amount = flt(grand_total) * (term.discount / 100) if term.discount_type == 'Percentage' else discount
|
||||||
|
term_details.outstanding = term_details.payment_amount
|
||||||
|
term_details.mode_of_payment = term.mode_of_payment
|
||||||
|
|
||||||
if bill_date:
|
if bill_date:
|
||||||
term_details.due_date = get_due_date(term, bill_date)
|
term_details.due_date = get_due_date(term, bill_date)
|
||||||
|
term_details.discount_date = get_discount_date(term, bill_date)
|
||||||
elif posting_date:
|
elif posting_date:
|
||||||
term_details.due_date = get_due_date(term, posting_date)
|
term_details.due_date = get_due_date(term, posting_date)
|
||||||
|
term_details.discount_date = get_discount_date(term, posting_date)
|
||||||
|
|
||||||
if getdate(term_details.due_date) < getdate(posting_date):
|
if getdate(term_details.due_date) < getdate(posting_date):
|
||||||
term_details.due_date = posting_date
|
term_details.due_date = posting_date
|
||||||
term_details.mode_of_payment = term.mode_of_payment
|
|
||||||
|
|
||||||
return term_details
|
return term_details
|
||||||
|
|
||||||
|
|
||||||
def get_due_date(term, posting_date=None, bill_date=None):
|
def get_due_date(term, posting_date=None, bill_date=None):
|
||||||
due_date = None
|
due_date = None
|
||||||
date = bill_date or posting_date
|
date = bill_date or posting_date
|
||||||
@ -1258,6 +1268,16 @@ def get_due_date(term, posting_date=None, bill_date=None):
|
|||||||
due_date = add_months(get_last_day(date), term.credit_months)
|
due_date = add_months(get_last_day(date), term.credit_months)
|
||||||
return due_date
|
return due_date
|
||||||
|
|
||||||
|
def get_discount_date(term, posting_date=None, bill_date=None):
|
||||||
|
discount_validity = None
|
||||||
|
date = bill_date or posting_date
|
||||||
|
if term.discount_validity_based_on == "Day(s) after invoice date":
|
||||||
|
discount_validity = add_days(date, term.discount_validity)
|
||||||
|
elif term.discount_validity_based_on == "Day(s) after the end of the invoice month":
|
||||||
|
discount_validity = add_days(get_last_day(date), term.discount_validity)
|
||||||
|
elif term.discount_validity_based_on == "Month(s) after the end of the invoice month":
|
||||||
|
discount_validity = add_months(get_last_day(date), term.discount_validity)
|
||||||
|
return discount_validity
|
||||||
|
|
||||||
def get_supplier_block_status(party_name):
|
def get_supplier_block_status(party_name):
|
||||||
"""
|
"""
|
||||||
@ -1316,25 +1336,63 @@ def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child
|
|||||||
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
|
||||||
child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
|
child_item = frappe.new_doc(child_doctype, p_doc, child_docname)
|
||||||
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
item = frappe.get_doc("Item", trans_item.get('item_code'))
|
||||||
|
|
||||||
for field in ("item_code", "item_name", "description", "item_group"):
|
for field in ("item_code", "item_name", "description", "item_group"):
|
||||||
child_item.update({field: item.get(field)})
|
child_item.update({field: item.get(field)})
|
||||||
|
|
||||||
date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
|
date_fieldname = "delivery_date" if child_doctype == "Sales Order Item" else "schedule_date"
|
||||||
child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
|
child_item.update({date_fieldname: trans_item.get(date_fieldname) or p_doc.get(date_fieldname)})
|
||||||
|
child_item.stock_uom = item.stock_uom
|
||||||
child_item.uom = trans_item.get("uom") or item.stock_uom
|
child_item.uom = trans_item.get("uom") or item.stock_uom
|
||||||
|
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||||
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor"))
|
||||||
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor
|
||||||
|
|
||||||
if child_doctype == "Purchase Order Item":
|
if child_doctype == "Purchase Order Item":
|
||||||
child_item.base_rate = 1 # Initiallize value will update in parent validation
|
# Initialized value will update in parent validation
|
||||||
child_item.base_amount = 1 # Initiallize value will update in parent validation
|
child_item.base_rate = 1
|
||||||
|
child_item.base_amount = 1
|
||||||
if child_doctype == "Sales Order Item":
|
if child_doctype == "Sales Order Item":
|
||||||
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
|
||||||
if not child_item.warehouse:
|
if not child_item.warehouse:
|
||||||
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.")
|
||||||
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
.format(frappe.bold("default warehouse"), frappe.bold(item.item_code)))
|
||||||
|
|
||||||
set_child_tax_template_and_map(item, child_item, p_doc)
|
set_child_tax_template_and_map(item, child_item, p_doc)
|
||||||
add_taxes_from_tax_template(child_item, p_doc)
|
add_taxes_from_tax_template(child_item, p_doc)
|
||||||
return child_item
|
return child_item
|
||||||
|
|
||||||
|
def validate_child_on_delete(row, parent):
|
||||||
|
"""Check if partially transacted item (row) is being deleted."""
|
||||||
|
if parent.doctype == "Sales Order":
|
||||||
|
if flt(row.delivered_qty):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(row.idx, row.item_code))
|
||||||
|
if flt(row.work_order_qty):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(row.idx, row.item_code))
|
||||||
|
if flt(row.ordered_qty):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(row.idx, row.item_code))
|
||||||
|
|
||||||
|
if parent.doctype == "Purchase Order" and flt(row.received_qty):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(row.idx, row.item_code))
|
||||||
|
|
||||||
|
if flt(row.billed_amt):
|
||||||
|
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(row.idx, row.item_code))
|
||||||
|
|
||||||
|
def update_bin_on_delete(row, doctype):
|
||||||
|
"""Update bin for deleted item (row)."""
|
||||||
|
from erpnext.stock.stock_balance import update_bin_qty, get_reserved_qty, get_ordered_qty, get_indented_qty
|
||||||
|
qty_dict = {}
|
||||||
|
|
||||||
|
if doctype == "Sales Order":
|
||||||
|
qty_dict["reserved_qty"] = get_reserved_qty(row.item_code, row.warehouse)
|
||||||
|
else:
|
||||||
|
if row.material_request_item:
|
||||||
|
qty_dict["indented_qty"] = get_indented_qty(row.item_code, row.warehouse)
|
||||||
|
|
||||||
|
qty_dict["ordered_qty"] = get_ordered_qty(row.item_code, row.warehouse)
|
||||||
|
|
||||||
|
update_bin_qty(row.item_code, row.warehouse, qty_dict)
|
||||||
|
|
||||||
def validate_and_delete_children(parent, data):
|
def validate_and_delete_children(parent, data):
|
||||||
deleted_children = []
|
deleted_children = []
|
||||||
updated_item_names = [d.get("docname") for d in data]
|
updated_item_names = [d.get("docname") for d in data]
|
||||||
@ -1343,23 +1401,17 @@ def validate_and_delete_children(parent, data):
|
|||||||
deleted_children.append(item)
|
deleted_children.append(item)
|
||||||
|
|
||||||
for d in deleted_children:
|
for d in deleted_children:
|
||||||
if parent.doctype == "Sales Order":
|
validate_child_on_delete(d, parent)
|
||||||
if flt(d.delivered_qty):
|
|
||||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code))
|
|
||||||
if flt(d.work_order_qty):
|
|
||||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(d.idx, d.item_code))
|
|
||||||
if flt(d.ordered_qty):
|
|
||||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(d.idx, d.item_code))
|
|
||||||
|
|
||||||
if parent.doctype == "Purchase Order" and flt(d.received_qty):
|
|
||||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
|
|
||||||
|
|
||||||
if flt(d.billed_amt):
|
|
||||||
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
|
|
||||||
|
|
||||||
d.cancel()
|
d.cancel()
|
||||||
d.delete()
|
d.delete()
|
||||||
|
|
||||||
|
# need to update ordered qty in Material Request first
|
||||||
|
# bin uses Material Request Items to recalculate & update
|
||||||
|
parent.update_prevdoc_status()
|
||||||
|
|
||||||
|
for d in deleted_children:
|
||||||
|
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'):
|
||||||
@ -1394,7 +1446,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
|||||||
)
|
)
|
||||||
|
|
||||||
def get_new_child_item(item_row):
|
def get_new_child_item(item_row):
|
||||||
child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
|
child_doctype = "Sales Order Item" if parent_doctype == "Sales Order" else "Purchase Order Item"
|
||||||
return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
|
return set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, item_row)
|
||||||
|
|
||||||
def validate_quantity(child_item, d):
|
def validate_quantity(child_item, d):
|
||||||
|
@ -144,7 +144,7 @@ class SellingController(StockController):
|
|||||||
|
|
||||||
if sales_person.commission_rate:
|
if sales_person.commission_rate:
|
||||||
sales_person.incentives = flt(
|
sales_person.incentives = flt(
|
||||||
sales_person.allocated_amount * flt(sales_person.commission_rate) / 100.0,
|
sales_person.allocated_amount * flt(sales_person.commission_rate) / 100.0,
|
||||||
self.precision("incentives", sales_person))
|
self.precision("incentives", sales_person))
|
||||||
|
|
||||||
total += sales_person.allocated_percentage
|
total += sales_person.allocated_percentage
|
||||||
@ -502,4 +502,4 @@ def set_default_income_account_for_item(obj):
|
|||||||
for d in obj.get("items"):
|
for d in obj.get("items"):
|
||||||
if d.item_code:
|
if d.item_code:
|
||||||
if getattr(d, "income_account", None):
|
if getattr(d, "income_account", None):
|
||||||
set_item_default(d.item_code, obj.company, 'income_account', d.income_account)
|
set_item_default(d.item_code, obj.company, 'income_account', d.income_account)
|
||||||
|
@ -495,7 +495,7 @@ class StockController(AccountsController):
|
|||||||
"voucher_no": self.name,
|
"voucher_no": self.name,
|
||||||
"company": self.company
|
"company": self.company
|
||||||
})
|
})
|
||||||
if check_if_future_sle_exists(args):
|
if future_sle_exists(args):
|
||||||
create_repost_item_valuation_entry(args)
|
create_repost_item_valuation_entry(args)
|
||||||
elif not is_reposting_pending():
|
elif not is_reposting_pending():
|
||||||
check_if_stock_and_account_balance_synced(self.posting_date,
|
check_if_stock_and_account_balance_synced(self.posting_date,
|
||||||
@ -506,37 +506,42 @@ def is_reposting_pending():
|
|||||||
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
{'docstatus': 1, 'status': ['in', ['Queued','In Progress']]})
|
||||||
|
|
||||||
|
|
||||||
def check_if_future_sle_exists(args):
|
def future_sle_exists(args):
|
||||||
sl_entries = frappe.db.get_all("Stock Ledger Entry",
|
sl_entries = frappe.get_all("Stock Ledger Entry",
|
||||||
filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
|
filters={"voucher_type": args.voucher_type, "voucher_no": args.voucher_no},
|
||||||
fields=["item_code", "warehouse"],
|
fields=["item_code", "warehouse"],
|
||||||
order_by="creation asc")
|
order_by="creation asc")
|
||||||
|
|
||||||
distinct_item_warehouses = list(set([(d.item_code, d.warehouse) for d in sl_entries]))
|
if not sl_entries:
|
||||||
|
return
|
||||||
|
|
||||||
sle_exists = False
|
warehouse_items_map = {}
|
||||||
for item_code, warehouse in distinct_item_warehouses:
|
for entry in sl_entries:
|
||||||
args.update({
|
if entry.warehouse not in warehouse_items_map:
|
||||||
"item_code": item_code,
|
warehouse_items_map[entry.warehouse] = set()
|
||||||
"warehouse": warehouse
|
|
||||||
})
|
warehouse_items_map[entry.warehouse].add(entry.item_code)
|
||||||
if get_sle(args):
|
|
||||||
sle_exists = True
|
or_conditions = []
|
||||||
break
|
for warehouse, items in warehouse_items_map.items():
|
||||||
return sle_exists
|
or_conditions.append(
|
||||||
|
"warehouse = '{}' and item_code in ({})".format(
|
||||||
|
warehouse,
|
||||||
|
", ".join(frappe.db.escape(item) for item in items)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def get_sle(args):
|
|
||||||
return frappe.db.sql("""
|
return frappe.db.sql("""
|
||||||
select name
|
select name
|
||||||
from `tabStock Ledger Entry`
|
from `tabStock Ledger Entry`
|
||||||
where
|
where
|
||||||
item_code=%(item_code)s
|
({})
|
||||||
and warehouse=%(warehouse)s
|
and timestamp(posting_date, posting_time)
|
||||||
and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
|
>= timestamp(%(posting_date)s, %(posting_time)s)
|
||||||
and voucher_no != %(voucher_no)s
|
and voucher_no != %(voucher_no)s
|
||||||
and is_cancelled = 0
|
and is_cancelled = 0
|
||||||
limit 1
|
limit 1
|
||||||
""", args)
|
""".format(" or ".join(or_conditions)), args)
|
||||||
|
|
||||||
def create_repost_item_valuation_entry(args):
|
def create_repost_item_valuation_entry(args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@ -554,4 +559,4 @@ def create_repost_item_valuation_entry(args):
|
|||||||
repost_entry.allow_zero_rate = args.allow_zero_rate
|
repost_entry.allow_zero_rate = args.allow_zero_rate
|
||||||
repost_entry.flags.ignore_links = True
|
repost_entry.flags.ignore_links = True
|
||||||
repost_entry.save()
|
repost_entry.save()
|
||||||
repost_entry.submit()
|
repost_entry.submit()
|
||||||
|
@ -109,11 +109,14 @@ class calculate_taxes_and_totals(object):
|
|||||||
elif item.discount_amount and item.pricing_rules:
|
elif item.discount_amount and item.pricing_rules:
|
||||||
item.rate = item.price_list_rate - item.discount_amount
|
item.rate = item.price_list_rate - item.discount_amount
|
||||||
|
|
||||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item']:
|
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item', 'Purchase Invoice Item', 'Purchase Order Item', 'Purchase Receipt Item']:
|
||||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||||
if flt(item.rate_with_margin) > 0:
|
if flt(item.rate_with_margin) > 0:
|
||||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||||
item.discount_amount = item.rate_with_margin - item.rate
|
if item.discount_amount and not item.discount_percentage:
|
||||||
|
item.rate -= item.discount_amount
|
||||||
|
else:
|
||||||
|
item.discount_amount = item.rate_with_margin - item.rate
|
||||||
elif flt(item.price_list_rate) > 0:
|
elif flt(item.price_list_rate) > 0:
|
||||||
item.discount_amount = item.price_list_rate - item.rate
|
item.discount_amount = item.price_list_rate - item.rate
|
||||||
elif flt(item.price_list_rate) > 0 and not item.discount_amount:
|
elif flt(item.price_list_rate) > 0 and not item.discount_amount:
|
||||||
@ -795,7 +798,7 @@ class init_landed_taxes_and_totals(object):
|
|||||||
for d in self.doc.get(self.tax_field):
|
for d in self.doc.get(self.tax_field):
|
||||||
if d.account_currency == company_currency:
|
if d.account_currency == company_currency:
|
||||||
d.exchange_rate = 1
|
d.exchange_rate = 1
|
||||||
elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
|
elif not d.exchange_rate:
|
||||||
d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
|
d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
|
||||||
account_currency=d.account_currency, company=self.doc.company)
|
account_currency=d.account_currency, company=self.doc.company)
|
||||||
|
|
||||||
@ -805,4 +808,4 @@ class init_landed_taxes_and_totals(object):
|
|||||||
def set_amounts_in_company_currency(self):
|
def set_amounts_in_company_currency(self):
|
||||||
for d in self.doc.get(self.tax_field):
|
for d in self.doc.get(self.tax_field):
|
||||||
d.amount = flt(d.amount, d.precision("amount"))
|
d.amount = flt(d.amount, d.precision("amount"))
|
||||||
d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
|
d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
|
||||||
|
@ -11,7 +11,8 @@ from frappe.utils.file_manager import get_file, get_file_path
|
|||||||
from six.moves.urllib.parse import urlencode
|
from six.moves.urllib.parse import urlencode
|
||||||
|
|
||||||
class LinkedInSettings(Document):
|
class LinkedInSettings(Document):
|
||||||
def get_authorization_url(self):
|
@frappe.whitelist()
|
||||||
|
def get_authorization_url(self):
|
||||||
params = urlencode({
|
params = urlencode({
|
||||||
"response_type":"code",
|
"response_type":"code",
|
||||||
"client_id": self.consumer_key,
|
"client_id": self.consumer_key,
|
||||||
@ -35,7 +36,7 @@ class LinkedInSettings(Document):
|
|||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
}
|
}
|
||||||
|
|
||||||
response = self.http_post(url=url, data=body, headers=headers)
|
response = self.http_post(url=url, data=body, headers=headers)
|
||||||
response = frappe.parse_json(response.content.decode())
|
response = frappe.parse_json(response.content.decode())
|
||||||
self.db_set("access_token", response["access_token"])
|
self.db_set("access_token", response["access_token"])
|
||||||
|
@ -85,6 +85,7 @@ class Opportunity(TransactionBase):
|
|||||||
self.opportunity_from = "Lead"
|
self.opportunity_from = "Lead"
|
||||||
self.party_name = lead_name
|
self.party_name = lead_name
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None):
|
||||||
if not self.has_active_quotation():
|
if not self.has_active_quotation():
|
||||||
frappe.db.set(self, 'status', 'Lost')
|
frappe.db.set(self, 'status', 'Lost')
|
||||||
@ -248,7 +249,6 @@ def make_quotation(source_name, target_doc=None):
|
|||||||
"doctype": "Quotation",
|
"doctype": "Quotation",
|
||||||
"field_map": {
|
"field_map": {
|
||||||
"opportunity_from": "quotation_to",
|
"opportunity_from": "quotation_to",
|
||||||
"opportunity_type": "order_type",
|
|
||||||
"name": "enq_no",
|
"name": "enq_no",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,7 @@ from frappe.utils import get_url_to_form, get_link_to_form
|
|||||||
from tweepy.error import TweepError
|
from tweepy.error import TweepError
|
||||||
|
|
||||||
class TwitterSettings(Document):
|
class TwitterSettings(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_authorize_url(self):
|
def get_authorize_url(self):
|
||||||
callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
|
callback_url = "{0}/api/method/erpnext.crm.doctype.twitter_settings.twitter_settings.callback?".format(frappe.utils.get_url())
|
||||||
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
|
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"), callback_url)
|
||||||
@ -21,12 +22,12 @@ class TwitterSettings(Document):
|
|||||||
frappe.msgprint(_("Error! Failed to get request token."))
|
frappe.msgprint(_("Error! Failed to get request token."))
|
||||||
frappe.throw(_('Invalid {0} or {1}').format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key")))
|
frappe.throw(_('Invalid {0} or {1}').format(frappe.bold("Consumer Key"), frappe.bold("Consumer Secret Key")))
|
||||||
|
|
||||||
|
|
||||||
def get_access_token(self, oauth_token, oauth_verifier):
|
def get_access_token(self, oauth_token, oauth_verifier):
|
||||||
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
||||||
auth.request_token = {
|
auth.request_token = {
|
||||||
'oauth_token' : oauth_token,
|
'oauth_token' : oauth_token,
|
||||||
'oauth_token_secret' : oauth_verifier
|
'oauth_token_secret' : oauth_verifier
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -50,10 +51,10 @@ class TwitterSettings(Document):
|
|||||||
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
|
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
|
||||||
|
|
||||||
def get_api(self, access_token, access_token_secret):
|
def get_api(self, access_token, access_token_secret):
|
||||||
# authentication of consumer key and secret
|
# authentication of consumer key and secret
|
||||||
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
|
||||||
# authentication of access token and secret
|
# authentication of access token and secret
|
||||||
auth.set_access_token(access_token, access_token_secret)
|
auth.set_access_token(access_token, access_token_secret)
|
||||||
|
|
||||||
return tweepy.API(auth)
|
return tweepy.API(auth)
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ class TwitterSettings(Document):
|
|||||||
if media:
|
if media:
|
||||||
media_id = self.upload_image(media)
|
media_id = self.upload_image(media)
|
||||||
return self.send_tweet(text, media_id)
|
return self.send_tweet(text, media_id)
|
||||||
|
|
||||||
def upload_image(self, media):
|
def upload_image(self, media):
|
||||||
media = get_file_path(media)
|
media = get_file_path(media)
|
||||||
api = self.get_api(self.access_token, self.access_token_secret)
|
api = self.get_api(self.access_token, self.access_token_secret)
|
||||||
|
@ -13,6 +13,7 @@ from erpnext.education.utils import OverlapError
|
|||||||
|
|
||||||
class CourseSchedulingTool(Document):
|
class CourseSchedulingTool(Document):
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def schedule_course(self):
|
def schedule_course(self):
|
||||||
"""Creates course schedules as per specified parameters"""
|
"""Creates course schedules as per specified parameters"""
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ class FeeSchedule(Document):
|
|||||||
self.grand_total = no_of_students*self.total_amount
|
self.grand_total = no_of_students*self.total_amount
|
||||||
self.grand_total_in_words = money_in_words(self.grand_total)
|
self.grand_total_in_words = money_in_words(self.grand_total)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_fees(self):
|
def create_fees(self):
|
||||||
self.db_set("fee_creation_status", "In Process")
|
self.db_set("fee_creation_status", "In Process")
|
||||||
frappe.publish_realtime("fee_schedule_progress",
|
frappe.publish_realtime("fee_schedule_progress",
|
||||||
|
@ -91,6 +91,8 @@ class ProgramEnrollment(Document):
|
|||||||
(fee, fee) for fee in fee_list]
|
(fee, fee) for fee in fee_list]
|
||||||
msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
|
msgprint(_("Fee Records Created - {0}").format(comma_and(fee_list)))
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_courses(self):
|
def get_courses(self):
|
||||||
return frappe.db.sql('''select course from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1)
|
return frappe.db.sql('''select course from `tabProgram Course` where parent = %s and required = 1''', (self.program), as_dict=1)
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ class ProgramEnrollmentTool(Document):
|
|||||||
academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
|
academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
|
||||||
self.set_onload("academic_term_reqd", academic_term_reqd)
|
self.set_onload("academic_term_reqd", academic_term_reqd)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_students(self):
|
def get_students(self):
|
||||||
students = []
|
students = []
|
||||||
if not self.get_students_from:
|
if not self.get_students_from:
|
||||||
@ -49,6 +50,7 @@ class ProgramEnrollmentTool(Document):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("No students Found"))
|
frappe.throw(_("No students Found"))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def enroll_students(self):
|
def enroll_students(self):
|
||||||
total = len(self.students)
|
total = len(self.students)
|
||||||
for i, stud in enumerate(self.students):
|
for i, stud in enumerate(self.students):
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"naming_series",
|
"naming_series",
|
||||||
"student",
|
"student",
|
||||||
"student_name",
|
"student_name",
|
||||||
|
"student_mobile_number",
|
||||||
"course_schedule",
|
"course_schedule",
|
||||||
"student_group",
|
"student_group",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
@ -93,11 +94,19 @@
|
|||||||
"options": "Student Attendance",
|
"options": "Student Attendance",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "student.student_mobile_number",
|
||||||
|
"fieldname": "student_mobile_number",
|
||||||
|
"fieldtype": "Read Only",
|
||||||
|
"label": "Student Mobile Number",
|
||||||
|
"options": "Phone"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-07-08 13:55:42.580181",
|
"modified": "2021-03-24 00:02:11.005895",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Student Attendance",
|
"name": "Student Attendance",
|
||||||
|
@ -9,6 +9,7 @@ from frappe.model.document import Document
|
|||||||
from erpnext.education.doctype.student_group.student_group import get_students
|
from erpnext.education.doctype.student_group.student_group import get_students
|
||||||
|
|
||||||
class StudentGroupCreationTool(Document):
|
class StudentGroupCreationTool(Document):
|
||||||
|
@frappe.whitelist()
|
||||||
def get_courses(self):
|
def get_courses(self):
|
||||||
group_list = []
|
group_list = []
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ class StudentGroupCreationTool(Document):
|
|||||||
|
|
||||||
return group_list
|
return group_list
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def create_student_groups(self):
|
def create_student_groups(self):
|
||||||
if not self.courses:
|
if not self.courses:
|
||||||
frappe.throw(_("""No Student Groups created."""))
|
frappe.throw(_("""No Student Groups created."""))
|
||||||
|
@ -59,9 +59,10 @@ class MpesaSettings(Document):
|
|||||||
request_amounts.append(amount)
|
request_amounts.append(amount)
|
||||||
else:
|
else:
|
||||||
request_amounts = [request_amount]
|
request_amounts = [request_amount]
|
||||||
|
|
||||||
return request_amounts
|
return request_amounts
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_account_balance_info(self):
|
def get_account_balance_info(self):
|
||||||
payload = dict(
|
payload = dict(
|
||||||
reference_doctype="Mpesa Settings",
|
reference_doctype="Mpesa Settings",
|
||||||
@ -198,7 +199,7 @@ def get_completed_integration_requests_info(reference_doctype, reference_docname
|
|||||||
completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
|
completed_mpesa_receipt = fetch_param_value(item_response, "MpesaReceiptNumber", "Name")
|
||||||
completed_payments.append(completed_amount)
|
completed_payments.append(completed_amount)
|
||||||
mpesa_receipts.append(completed_mpesa_receipt)
|
mpesa_receipts.append(completed_mpesa_receipt)
|
||||||
|
|
||||||
return mpesa_receipts, completed_payments
|
return mpesa_receipts, completed_payments
|
||||||
|
|
||||||
def get_account_balance(request_payload):
|
def get_account_balance(request_payload):
|
||||||
|
@ -15,6 +15,7 @@ from frappe.utils import add_months, formatdate, getdate, today
|
|||||||
|
|
||||||
class PlaidSettings(Document):
|
class PlaidSettings(Document):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@frappe.whitelist()
|
||||||
def get_link_token():
|
def get_link_token():
|
||||||
plaid = PlaidConnector()
|
plaid = PlaidConnector()
|
||||||
return plaid.get_link_token()
|
return plaid.get_link_token()
|
||||||
|
@ -23,14 +23,9 @@ class TestPlaidSettings(unittest.TestCase):
|
|||||||
doc.cancel()
|
doc.cancel()
|
||||||
doc.delete()
|
doc.delete()
|
||||||
|
|
||||||
for ba in frappe.get_all("Bank Account"):
|
for doctype in ("Bank Account", "Bank Account Type", "Bank Account Subtype"):
|
||||||
frappe.get_doc("Bank Account", ba.name).delete()
|
for d in frappe.get_all(doctype):
|
||||||
|
frappe.delete_doc(doctype, d.name, force=True)
|
||||||
for at in frappe.get_all("Bank Account Type"):
|
|
||||||
frappe.get_doc("Bank Account Type", at.name).delete()
|
|
||||||
|
|
||||||
for ast in frappe.get_all("Bank Account Subtype"):
|
|
||||||
frappe.get_doc("Bank Account Subtype", ast.name).delete()
|
|
||||||
|
|
||||||
def test_plaid_disabled(self):
|
def test_plaid_disabled(self):
|
||||||
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
frappe.db.set_value("Plaid Settings", None, "enabled", 0)
|
||||||
|
@ -54,6 +54,7 @@ class QuickBooksMigrator(Document):
|
|||||||
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def migrate(self):
|
def migrate(self):
|
||||||
frappe.enqueue_doc("QuickBooks Migrator", "QuickBooks Migrator", "_migrate", queue="long")
|
frappe.enqueue_doc("QuickBooks Migrator", "QuickBooks Migrator", "_migrate", queue="long")
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
|
|
||||||
import unittest, os, json
|
import unittest, os, json
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr, cint
|
||||||
from erpnext.erpnext_integrations.connectors.shopify_connection import create_order
|
from erpnext.erpnext_integrations.connectors.shopify_connection import create_order
|
||||||
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item
|
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item
|
||||||
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
|
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
|
||||||
@ -13,9 +13,14 @@ from frappe.core.doctype.data_import.data_import import import_doc
|
|||||||
|
|
||||||
|
|
||||||
class ShopifySettings(unittest.TestCase):
|
class ShopifySettings(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
|
|
||||||
|
cls.allow_negative_stock = cint(frappe.db.get_value('Stock Settings', None, 'allow_negative_stock'))
|
||||||
|
if not cls.allow_negative_stock:
|
||||||
|
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
|
||||||
|
|
||||||
# use the fixture data
|
# use the fixture data
|
||||||
import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
|
import_doc(frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json"))
|
||||||
|
|
||||||
@ -24,9 +29,15 @@ class ShopifySettings(unittest.TestCase):
|
|||||||
frappe.reload_doctype("Delivery Note")
|
frappe.reload_doctype("Delivery Note")
|
||||||
frappe.reload_doctype("Sales Invoice")
|
frappe.reload_doctype("Sales Invoice")
|
||||||
|
|
||||||
self.setup_shopify()
|
cls.setup_shopify()
|
||||||
|
|
||||||
def setup_shopify(self):
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
if not cls.allow_negative_stock:
|
||||||
|
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setup_shopify(cls):
|
||||||
shopify_settings = frappe.get_doc("Shopify Settings")
|
shopify_settings = frappe.get_doc("Shopify Settings")
|
||||||
shopify_settings.taxes = []
|
shopify_settings.taxes = []
|
||||||
|
|
||||||
@ -56,21 +67,20 @@ class ShopifySettings(unittest.TestCase):
|
|||||||
"delivery_note_series": "DN-"
|
"delivery_note_series": "DN-"
|
||||||
}).save(ignore_permissions=True)
|
}).save(ignore_permissions=True)
|
||||||
|
|
||||||
self.shopify_settings = shopify_settings
|
cls.shopify_settings = shopify_settings
|
||||||
|
|
||||||
def test_order(self):
|
def test_order(self):
|
||||||
### Create Customer ###
|
# Create Customer
|
||||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
|
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
|
||||||
shopify_customer = json.load(shopify_customer)
|
shopify_customer = json.load(shopify_customer)
|
||||||
create_customer(shopify_customer.get("customer"), self.shopify_settings)
|
create_customer(shopify_customer.get("customer"), self.shopify_settings)
|
||||||
|
|
||||||
### Create Item ###
|
# Create Item
|
||||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item:
|
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item:
|
||||||
shopify_item = json.load(shopify_item)
|
shopify_item = json.load(shopify_item)
|
||||||
make_item("_Test Warehouse - _TC", shopify_item.get("product"))
|
make_item("_Test Warehouse - _TC", shopify_item.get("product"))
|
||||||
|
|
||||||
|
# Create Order
|
||||||
### Create Order ###
|
|
||||||
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
|
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
|
||||||
shopify_order = json.load(shopify_order)
|
shopify_order = json.load(shopify_order)
|
||||||
|
|
||||||
@ -80,17 +90,17 @@ class ShopifySettings(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id)
|
self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id)
|
||||||
|
|
||||||
#check for customer
|
# Check for customer
|
||||||
shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id"))
|
shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id"))
|
||||||
sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id")
|
sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id")
|
||||||
|
|
||||||
self.assertEqual(shopify_order_customer_id, sales_order_customer_id)
|
self.assertEqual(shopify_order_customer_id, sales_order_customer_id)
|
||||||
|
|
||||||
#check sales invoice
|
# Check sales invoice
|
||||||
sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id})
|
sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id})
|
||||||
self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total)
|
self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total)
|
||||||
|
|
||||||
#check delivery note
|
# Check delivery note
|
||||||
delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note`
|
delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note`
|
||||||
where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0]
|
where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0]
|
||||||
|
|
||||||
|
@ -594,18 +594,22 @@ class TallyMigration(Document):
|
|||||||
frappe.db.set_value("Price List", "Tally Price List", "enabled", 0)
|
frappe.db.set_value("Price List", "Tally Price List", "enabled", 0)
|
||||||
frappe.flags.in_migrate = False
|
frappe.flags.in_migrate = False
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_master_data(self):
|
def process_master_data(self):
|
||||||
self.set_status("Processing Master Data")
|
self.set_status("Processing Master Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def import_master_data(self):
|
def import_master_data(self):
|
||||||
self.set_status("Importing Master Data")
|
self.set_status("Importing Master Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_master_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def process_day_book_data(self):
|
def process_day_book_data(self):
|
||||||
self.set_status("Processing Day Book Data")
|
self.set_status("Processing Day Book Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_process_day_book_data", queue="long", timeout=3600)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def import_day_book_data(self):
|
def import_day_book_data(self):
|
||||||
self.set_status("Importing Day Book Data")
|
self.set_status("Importing Day Book Data")
|
||||||
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
frappe.enqueue_doc(self.doctype, self.name, "_import_day_book_data", queue="long", timeout=3600)
|
||||||
|
@ -54,6 +54,7 @@ class ClinicalProcedure(Document):
|
|||||||
def set_title(self):
|
def set_title(self):
|
||||||
self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
|
self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def complete_procedure(self):
|
def complete_procedure(self):
|
||||||
if self.consume_stock and self.items:
|
if self.consume_stock and self.items:
|
||||||
stock_entry = make_stock_entry(self)
|
stock_entry = make_stock_entry(self)
|
||||||
@ -96,6 +97,7 @@ class ClinicalProcedure(Document):
|
|||||||
if self.consume_stock and self.items:
|
if self.consume_stock and self.items:
|
||||||
return stock_entry
|
return stock_entry
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def start_procedure(self):
|
def start_procedure(self):
|
||||||
allow_start = self.set_actual_qty()
|
allow_start = self.set_actual_qty()
|
||||||
if allow_start:
|
if allow_start:
|
||||||
@ -116,6 +118,7 @@ class ClinicalProcedure(Document):
|
|||||||
|
|
||||||
return allow_start
|
return allow_start
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def make_material_receipt(self, submit=False):
|
def make_material_receipt(self, submit=False):
|
||||||
stock_entry = frappe.new_doc('Stock Entry')
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ class InpatientMedicationEntry(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_medication_orders()
|
self.validate_medication_orders()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_medication_orders(self):
|
def get_medication_orders(self):
|
||||||
# pull inpatient medication orders based on selected filters
|
# pull inpatient medication orders based on selected filters
|
||||||
orders = get_pending_medication_orders(self)
|
orders = get_pending_medication_orders(self)
|
||||||
|
@ -57,6 +57,7 @@ class InpatientMedicationOrder(Document):
|
|||||||
|
|
||||||
self.db_set('status', status)
|
self.db_set('status', status)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def add_order_entries(self, order):
|
def add_order_entries(self, order):
|
||||||
if order.get('drug_code'):
|
if order.get('drug_code'):
|
||||||
dosage = frappe.get_doc('Prescription Dosage', order.get('dosage'))
|
dosage = frappe.get_doc('Prescription Dosage', order.get('dosage'))
|
||||||
|
@ -81,15 +81,8 @@ class TestInpatientMedicationOrder(unittest.TestCase):
|
|||||||
self.ip_record.reload()
|
self.ip_record.reload()
|
||||||
discharge_patient(self.ip_record)
|
discharge_patient(self.ip_record)
|
||||||
|
|
||||||
for entry in frappe.get_all('Inpatient Medication Entry'):
|
for doctype in ["Inpatient Medication Entry", "Inpatient Medication Order"]:
|
||||||
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype))
|
||||||
doc.cancel()
|
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
for entry in frappe.get_all('Inpatient Medication Order'):
|
|
||||||
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
|
||||||
doc.cancel()
|
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
def create_dosage_form():
|
def create_dosage_form():
|
||||||
if not frappe.db.exists('Dosage Form', 'Tablet'):
|
if not frappe.db.exists('Dosage Form', 'Tablet'):
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
"discharge_ordered_date",
|
"discharge_ordered_date",
|
||||||
"discharge_practitioner",
|
"discharge_practitioner",
|
||||||
"discharge_encounter",
|
"discharge_encounter",
|
||||||
"discharge_date",
|
"discharge_datetime",
|
||||||
"cb_discharge",
|
"cb_discharge",
|
||||||
"discharge_instructions",
|
"discharge_instructions",
|
||||||
"followup_date",
|
"followup_date",
|
||||||
@ -404,14 +404,15 @@
|
|||||||
"permlevel": 1
|
"permlevel": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "discharge_date",
|
"fieldname": "discharge_datetime",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Datetime",
|
||||||
"label": "Discharge Date",
|
"label": "Discharge Date",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-21 02:26:22.144575",
|
"modified": "2021-03-18 14:44:11.689956",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "Inpatient Record",
|
"name": "Inpatient Record",
|
||||||
|
@ -53,12 +53,15 @@ class InpatientRecord(Document):
|
|||||||
+ """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
+ """ <b><a href="/app/Form/Inpatient Record/{0}">{0}</a></b>""".format(ip_record[0].name))
|
||||||
frappe.throw(msg)
|
frappe.throw(msg)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def admit(self, service_unit, check_in, expected_discharge=None):
|
def admit(self, service_unit, check_in, expected_discharge=None):
|
||||||
admit_patient(self, service_unit, check_in, expected_discharge)
|
admit_patient(self, service_unit, check_in, expected_discharge)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def discharge(self):
|
def discharge(self):
|
||||||
discharge_patient(self)
|
discharge_patient(self)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def transfer(self, service_unit, check_in, leave_from):
|
def transfer(self, service_unit, check_in, leave_from):
|
||||||
if leave_from:
|
if leave_from:
|
||||||
patient_leave_service_unit(self, check_in, leave_from)
|
patient_leave_service_unit(self, check_in, leave_from)
|
||||||
@ -151,7 +154,7 @@ def check_out_inpatient(inpatient_record):
|
|||||||
|
|
||||||
def discharge_patient(inpatient_record):
|
def discharge_patient(inpatient_record):
|
||||||
validate_inpatient_invoicing(inpatient_record)
|
validate_inpatient_invoicing(inpatient_record)
|
||||||
inpatient_record.discharge_date = today()
|
inpatient_record.discharge_datetime = now_datetime()
|
||||||
inpatient_record.status = "Discharged"
|
inpatient_record.status = "Discharged"
|
||||||
|
|
||||||
inpatient_record.save(ignore_permissions = True)
|
inpatient_record.save(ignore_permissions = True)
|
||||||
|
@ -111,6 +111,7 @@ class Patient(Document):
|
|||||||
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
age_str = str(age.years) + ' ' + _("Years(s)") + ' ' + str(age.months) + ' ' + _("Month(s)") + ' ' + str(age.days) + ' ' + _("Day(s)")
|
||||||
return age_str
|
return age_str
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def invoice_patient_registration(self):
|
def invoice_patient_registration(self):
|
||||||
if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
|
if frappe.db.get_single_value('Healthcare Settings', 'registration_fee'):
|
||||||
company = frappe.defaults.get_user_default('company')
|
company = frappe.defaults.get_user_default('company')
|
||||||
|
@ -113,6 +113,7 @@ class PatientAppointment(Document):
|
|||||||
if fee_validity:
|
if fee_validity:
|
||||||
frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
|
frappe.msgprint(_('{0} has fee validity till {1}').format(self.patient, fee_validity.valid_till))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_therapy_types(self):
|
def get_therapy_types(self):
|
||||||
if not self.therapy_plan:
|
if not self.therapy_plan:
|
||||||
return
|
return
|
||||||
|
@ -34,6 +34,7 @@ class PatientHistorySettings(Document):
|
|||||||
frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
|
frappe.throw(_('Row #{0}: Field {1} in Document Type {2} is not a Date / Datetime field.').format(
|
||||||
entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
|
entry.idx, frappe.bold(entry.date_fieldname), frappe.bold(entry.document_type)))
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_doctype_fields(self, document_type, fields):
|
def get_doctype_fields(self, document_type, fields):
|
||||||
multicheck_fields = []
|
multicheck_fields = []
|
||||||
doc_fields = frappe.get_meta(document_type).fields
|
doc_fields = frappe.get_meta(document_type).fields
|
||||||
@ -49,6 +50,7 @@ class PatientHistorySettings(Document):
|
|||||||
|
|
||||||
return multicheck_fields
|
return multicheck_fields
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def get_date_field_for_dt(self, document_type):
|
def get_date_field_for_dt(self, document_type):
|
||||||
meta = frappe.get_meta(document_type)
|
meta = frappe.get_meta(document_type)
|
||||||
date_fields = meta.get('fields', {
|
date_fields = meta.get('fields', {
|
||||||
|
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