Merge branch 'develop' into drop-shipping
This commit is contained in:
commit
8451da7924
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
|
4
.github/helper/install.sh
vendored
4
.github/helper/install.sh
vendored
@ -12,7 +12,7 @@ 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
|
||||||
@ -43,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
|
||||||
|
6
.github/workflows/ci-tests.yml
vendored
6
.github/workflows/ci-tests.yml
vendored
@ -1,12 +1,10 @@
|
|||||||
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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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"));
|
||||||
},
|
},
|
||||||
@ -1066,11 +1076,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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
@ -377,6 +377,12 @@ class POSInvoice(SalesInvoice):
|
|||||||
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
"allow_print_before_pay": profile.get("allow_print_before_pay")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
@ -400,7 +406,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,16 @@ 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):
|
||||||
|
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 +378,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 +419,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 +427,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 +446,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 +465,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 +514,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`")
|
||||||
|
|
||||||
|
|
||||||
|
@ -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",
|
||||||
@ -458,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",
|
||||||
@ -552,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": "2021-03-01 23:18:38.717613",
|
"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()
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
|
||||||
@ -2269,4 +2278,4 @@ def add_taxes(doc):
|
|||||||
"cost_center": "Main - TCP1",
|
"cost_center": "Main - TCP1",
|
||||||
"description": "Excise Duty",
|
"description": "Excise Duty",
|
||||||
"rate": 12
|
"rate": 12
|
||||||
})
|
})
|
||||||
|
@ -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)))
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -723,7 +723,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",
|
||||||
|
@ -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):
|
||||||
@ -1394,7 +1395,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()
|
||||||
|
@ -113,7 +113,10 @@ class calculate_taxes_and_totals(object):
|
|||||||
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 not item.discount_amount:
|
||||||
|
item.discount_amount = item.rate_with_margin - item.rate
|
||||||
|
elif not item.discount_percentage:
|
||||||
|
item.rate -= item.discount_amount
|
||||||
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)
|
||||||
|
|
||||||
|
@ -248,7 +248,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",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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",
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
@ -151,7 +151,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)
|
||||||
|
@ -195,6 +195,10 @@ sounds = [
|
|||||||
{"name": "call-disconnect", "src": "/assets/erpnext/sounds/call-disconnect.mp3", "volume": 0.2},
|
{"name": "call-disconnect", "src": "/assets/erpnext/sounds/call-disconnect.mp3", "volume": 0.2},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
has_upload_permission = {
|
||||||
|
"Employee": "erpnext.hr.doctype.employee.employee.has_upload_permission"
|
||||||
|
}
|
||||||
|
|
||||||
has_website_permission = {
|
has_website_permission = {
|
||||||
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
"Sales Order": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||||
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
"Quotation": "erpnext.controllers.website_list_for_contact.has_website_permission",
|
||||||
@ -324,6 +328,7 @@ scheduler_events = {
|
|||||||
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
|
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
|
||||||
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
|
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
|
||||||
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
|
"erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
|
||||||
|
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries"
|
||||||
],
|
],
|
||||||
"daily": [
|
"daily": [
|
||||||
"erpnext.stock.reorder_item.reorder_item",
|
"erpnext.stock.reorder_item.reorder_item",
|
||||||
|
@ -8,6 +8,8 @@ import unittest
|
|||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
|
test_dependencies = ["Employee"]
|
||||||
|
|
||||||
class TestAttendanceRequest(unittest.TestCase):
|
class TestAttendanceRequest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
for doctype in ["Attendance Request", "Attendance"]:
|
for doctype in ["Attendance Request", "Attendance"]:
|
||||||
@ -56,4 +58,4 @@ class TestAttendanceRequest(unittest.TestCase):
|
|||||||
self.assertEqual(attendance.docstatus, 2)
|
self.assertEqual(attendance.docstatus, 2)
|
||||||
|
|
||||||
def get_employee():
|
def get_employee():
|
||||||
return frappe.get_doc("Employee", "_T-Employee-00001")
|
return frappe.get_doc("Employee", "_T-Employee-00001")
|
||||||
|
@ -10,6 +10,8 @@ from erpnext.hr.doctype.attendance_request.test_attendance_request import get_em
|
|||||||
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period
|
||||||
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on
|
||||||
|
|
||||||
|
test_dependencies = ["Employee"]
|
||||||
|
|
||||||
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
class TestCompensatoryLeaveRequest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
frappe.db.sql(''' delete from `tabCompensatory Leave Request`''')
|
||||||
@ -129,4 +131,4 @@ def create_holiday_list():
|
|||||||
],
|
],
|
||||||
"holiday_list_name": "_Test Compensatory Leave"
|
"holiday_list_name": "_Test Compensatory Leave"
|
||||||
})
|
})
|
||||||
holiday_list.save()
|
holiday_list.save()
|
||||||
|
@ -8,7 +8,7 @@ from frappe.utils import getdate, validate_email_address, today, add_years, form
|
|||||||
from frappe.model.naming import set_name_by_naming_series
|
from frappe.model.naming import set_name_by_naming_series
|
||||||
from frappe import throw, _, scrub
|
from frappe import throw, _, scrub
|
||||||
from frappe.permissions import add_user_permission, remove_user_permission, \
|
from frappe.permissions import add_user_permission, remove_user_permission, \
|
||||||
set_user_permission_if_allowed, has_permission
|
set_user_permission_if_allowed, has_permission, get_doc_permissions
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from erpnext.utilities.transaction_base import delete_events
|
from erpnext.utilities.transaction_base import delete_events
|
||||||
from frappe.utils.nestedset import NestedSet
|
from frappe.utils.nestedset import NestedSet
|
||||||
@ -66,7 +66,7 @@ class Employee(NestedSet):
|
|||||||
def validate_user_details(self):
|
def validate_user_details(self):
|
||||||
data = frappe.db.get_value('User',
|
data = frappe.db.get_value('User',
|
||||||
self.user_id, ['enabled', 'user_image'], as_dict=1)
|
self.user_id, ['enabled', 'user_image'], as_dict=1)
|
||||||
if data.get("user_image"):
|
if data.get("user_image") and self.image == '':
|
||||||
self.image = data.get("user_image")
|
self.image = data.get("user_image")
|
||||||
self.validate_for_enabled_user_id(data.get("enabled", 0))
|
self.validate_for_enabled_user_id(data.get("enabled", 0))
|
||||||
self.validate_duplicate_user_id()
|
self.validate_duplicate_user_id()
|
||||||
@ -501,3 +501,10 @@ def has_user_permission_for_employee(user_name, employee_name):
|
|||||||
'allow': 'Employee',
|
'allow': 'Employee',
|
||||||
'for_value': employee_name
|
'for_value': employee_name
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def has_upload_permission(doc, ptype='read', user=None):
|
||||||
|
if not user:
|
||||||
|
user = frappe.session.user
|
||||||
|
if get_doc_permissions(doc, user=user, ptype=ptype).get(ptype):
|
||||||
|
return True
|
||||||
|
return doc.user_id == user
|
@ -13,6 +13,7 @@
|
|||||||
"stop_birthday_reminders",
|
"stop_birthday_reminders",
|
||||||
"expense_approver_mandatory_in_expense_claim",
|
"expense_approver_mandatory_in_expense_claim",
|
||||||
"leave_settings",
|
"leave_settings",
|
||||||
|
"send_leave_notification",
|
||||||
"leave_approval_notification_template",
|
"leave_approval_notification_template",
|
||||||
"leave_status_notification_template",
|
"leave_status_notification_template",
|
||||||
"role_allowed_to_create_backdated_leave_application",
|
"role_allowed_to_create_backdated_leave_application",
|
||||||
@ -69,15 +70,19 @@
|
|||||||
"label": "Leave Settings"
|
"label": "Leave Settings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.send_leave_notification == 1",
|
||||||
"fieldname": "leave_approval_notification_template",
|
"fieldname": "leave_approval_notification_template",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Leave Approval Notification Template",
|
"label": "Leave Approval Notification Template",
|
||||||
|
"mandatory_depends_on": "eval: doc.send_leave_notification == 1",
|
||||||
"options": "Email Template"
|
"options": "Email Template"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.send_leave_notification == 1",
|
||||||
"fieldname": "leave_status_notification_template",
|
"fieldname": "leave_status_notification_template",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Leave Status Notification Template",
|
"label": "Leave Status Notification Template",
|
||||||
|
"mandatory_depends_on": "eval: doc.send_leave_notification == 1",
|
||||||
"options": "Email Template"
|
"options": "Email Template"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -132,13 +137,19 @@
|
|||||||
"fieldname": "automatically_allocate_leaves_based_on_leave_policy",
|
"fieldname": "automatically_allocate_leaves_based_on_leave_policy",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Automatically Allocate Leaves Based On Leave Policy"
|
"label": "Automatically Allocate Leaves Based On Leave Policy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "send_leave_notification",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Send Leave Notification"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-cog",
|
"icon": "fa fa-cog",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-02-25 12:31:14.947865",
|
"modified": "2021-03-14 02:04:22.907159",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR Settings",
|
"name": "HR Settings",
|
||||||
|
@ -13,11 +13,21 @@ class TestJobApplicant(unittest.TestCase):
|
|||||||
|
|
||||||
def create_job_applicant(**args):
|
def create_job_applicant(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
job_applicant = frappe.get_doc({
|
|
||||||
"doctype": "Job Applicant",
|
filters = {
|
||||||
"applicant_name": args.applicant_name or "_Test Applicant",
|
"applicant_name": args.applicant_name or "_Test Applicant",
|
||||||
"email_id": args.email_id or "test_applicant@example.com",
|
"email_id": args.email_id or "test_applicant@example.com",
|
||||||
|
}
|
||||||
|
|
||||||
|
if frappe.db.exists("Job Applicant", filters):
|
||||||
|
return frappe.get_doc("Job Applicant", filters)
|
||||||
|
|
||||||
|
job_applicant = frappe.get_doc({
|
||||||
|
"doctype": "Job Applicant",
|
||||||
"status": args.status or "Open"
|
"status": args.status or "Open"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
job_applicant.update(filters)
|
||||||
job_applicant.save()
|
job_applicant.save()
|
||||||
return job_applicant
|
|
||||||
|
return job_applicant
|
||||||
|
@ -13,14 +13,15 @@ from erpnext.hr.doctype.staffing_plan.test_staffing_plan import make_company
|
|||||||
|
|
||||||
class TestJobOffer(unittest.TestCase):
|
class TestJobOffer(unittest.TestCase):
|
||||||
def test_job_offer_creation_against_vacancies(self):
|
def test_job_offer_creation_against_vacancies(self):
|
||||||
create_staffing_plan(staffing_details=[{
|
frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
|
||||||
"designation": "Designer",
|
job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
|
||||||
|
job_offer = create_job_offer(job_applicant=job_applicant.name, designation="UX Designer")
|
||||||
|
|
||||||
|
create_staffing_plan(name='Test No Vacancies', staffing_details=[{
|
||||||
|
"designation": "UX Designer",
|
||||||
"vacancies": 0,
|
"vacancies": 0,
|
||||||
"estimated_cost_per_position": 5000
|
"estimated_cost_per_position": 5000
|
||||||
}])
|
}])
|
||||||
frappe.db.set_value("HR Settings", None, "check_vacancies", 1)
|
|
||||||
job_applicant = create_job_applicant(email_id="test_job_offer@example.com")
|
|
||||||
job_offer = create_job_offer(job_applicant=job_applicant.name, designation="Researcher")
|
|
||||||
self.assertRaises(frappe.ValidationError, job_offer.submit)
|
self.assertRaises(frappe.ValidationError, job_offer.submit)
|
||||||
|
|
||||||
# test creation of job offer when vacancies are not present
|
# test creation of job offer when vacancies are not present
|
||||||
|
@ -6,6 +6,10 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
|||||||
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
|
from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import process_expired_allocation, expire_allocation
|
||||||
|
|
||||||
class TestLeaveAllocation(unittest.TestCase):
|
class TestLeaveAllocation(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
frappe.db.sql("delete from `tabLeave Period`")
|
||||||
|
|
||||||
def test_overlapping_allocation(self):
|
def test_overlapping_allocation(self):
|
||||||
frappe.db.sql("delete from `tabLeave Allocation`")
|
frappe.db.sql("delete from `tabLeave Allocation`")
|
||||||
|
|
||||||
@ -177,4 +181,4 @@ def create_leave_allocation(**args):
|
|||||||
})
|
})
|
||||||
return leave_allocation
|
return leave_allocation
|
||||||
|
|
||||||
test_dependencies = ["Employee", "Leave Type"]
|
test_dependencies = ["Employee", "Leave Type"]
|
||||||
|
@ -40,7 +40,8 @@ class LeaveApplication(Document):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
if self.status == "Open" and self.docstatus < 1:
|
if self.status == "Open" and self.docstatus < 1:
|
||||||
# notify leave approver about creation
|
# notify leave approver about creation
|
||||||
self.notify_leave_approver()
|
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||||
|
self.notify_leave_approver()
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.status == "Open":
|
if self.status == "Open":
|
||||||
@ -50,7 +51,8 @@ class LeaveApplication(Document):
|
|||||||
self.update_attendance()
|
self.update_attendance()
|
||||||
|
|
||||||
# notify leave applier about approval
|
# notify leave applier about approval
|
||||||
self.notify_employee()
|
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||||
|
self.notify_employee()
|
||||||
self.create_leave_ledger_entry()
|
self.create_leave_ledger_entry()
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
@ -60,7 +62,8 @@ class LeaveApplication(Document):
|
|||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.create_leave_ledger_entry(submit=False)
|
self.create_leave_ledger_entry(submit=False)
|
||||||
# notify leave applier about cancellation
|
# notify leave applier about cancellation
|
||||||
self.notify_employee()
|
if frappe.db.get_single_value("HR Settings", "send_leave_notification"):
|
||||||
|
self.notify_employee()
|
||||||
self.cancel_attendance()
|
self.cancel_attendance()
|
||||||
|
|
||||||
def validate_applicable_after(self):
|
def validate_applicable_after(self):
|
||||||
|
@ -12,7 +12,7 @@ from erpnext.hr.doctype.leave_type.test_leave_type import create_leave_type
|
|||||||
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
|
from erpnext.hr.doctype.leave_allocation.test_leave_allocation import create_leave_allocation
|
||||||
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
||||||
|
|
||||||
test_dependencies = ["Leave Allocation", "Leave Block List"]
|
test_dependencies = ["Leave Allocation", "Leave Block List", "Employee"]
|
||||||
|
|
||||||
_test_records = [
|
_test_records = [
|
||||||
{
|
{
|
||||||
@ -639,4 +639,4 @@ def allocate_leaves(employee, leave_period, leave_type, new_leaves_allocated, el
|
|||||||
"docstatus": 1
|
"docstatus": 1
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
allocate_leave.submit()
|
allocate_leave.submit()
|
||||||
|
@ -9,6 +9,8 @@ from erpnext.hr.doctype.leave_application.test_leave_application import get_leav
|
|||||||
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
from erpnext.hr.doctype.leave_policy_assignment.leave_policy_assignment import create_assignment_for_multiple_employees
|
||||||
from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
|
from erpnext.hr.doctype.leave_policy.test_leave_policy import create_leave_policy
|
||||||
|
|
||||||
|
test_dependencies = ["Employee"]
|
||||||
|
|
||||||
class TestLeavePolicyAssignment(unittest.TestCase):
|
class TestLeavePolicyAssignment(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -7,6 +7,8 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
from frappe.utils import nowdate, add_days
|
from frappe.utils import nowdate, add_days
|
||||||
|
|
||||||
|
test_dependencies = ["Shift Type"]
|
||||||
|
|
||||||
class TestShiftRequest(unittest.TestCase):
|
class TestShiftRequest(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
for doctype in ["Shift Request", "Shift Assignment"]:
|
for doctype in ["Shift Request", "Shift Assignment"]:
|
||||||
@ -46,4 +48,4 @@ def set_shift_approver(department):
|
|||||||
department_doc = frappe.get_doc("Department", department)
|
department_doc = frappe.get_doc("Department", department)
|
||||||
department_doc.append('shift_request_approver',{'approver': "test1@example.com"})
|
department_doc.append('shift_request_approver',{'approver': "test1@example.com"})
|
||||||
department_doc.save()
|
department_doc.save()
|
||||||
department_doc.reload()
|
department_doc.reload()
|
||||||
|
8
erpnext/hr/doctype/shift_type/test_records.json
Normal file
8
erpnext/hr/doctype/shift_type/test_records.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"doctype": "Shift Type",
|
||||||
|
"name": "Day Shift",
|
||||||
|
"start_time": "9:00:00",
|
||||||
|
"end_time": "18:00:00"
|
||||||
|
}
|
||||||
|
]
|
@ -7,14 +7,4 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestShiftType(unittest.TestCase):
|
class TestShiftType(unittest.TestCase):
|
||||||
def test_make_shift_type(self):
|
pass
|
||||||
if frappe.db.exists("Shift Type", "Day Shift"):
|
|
||||||
return
|
|
||||||
shift_type = frappe.get_doc({
|
|
||||||
"doctype": "Shift Type",
|
|
||||||
"name": "Day Shift",
|
|
||||||
"start_time": "9:00:00",
|
|
||||||
"end_time": "18:00:00"
|
|
||||||
})
|
|
||||||
shift_type.insert()
|
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ class StaffingPlan(Document):
|
|||||||
detail.current_count = designation_counts['employee_count']
|
detail.current_count = designation_counts['employee_count']
|
||||||
detail.current_openings = designation_counts['job_openings']
|
detail.current_openings = designation_counts['job_openings']
|
||||||
|
|
||||||
|
detail.total_estimated_cost = 0
|
||||||
if detail.number_of_positions > 0:
|
if detail.number_of_positions > 0:
|
||||||
if detail.vacancies > 0 and detail.estimated_cost_per_position:
|
if detail.vacancies > 0 and detail.estimated_cost_per_position:
|
||||||
detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
|
detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
|
||||||
|
@ -31,7 +31,7 @@ def get_columns():
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"fieldname": "job_opening",
|
"fieldname": "job_opening",
|
||||||
"options": "Job Opening",
|
"options": "Job Opening",
|
||||||
"width": 100
|
"width": 105
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Job Applicant"),
|
"label": _("Job Applicant"),
|
||||||
@ -44,13 +44,13 @@ def get_columns():
|
|||||||
"label": _("Applicant name"),
|
"label": _("Applicant name"),
|
||||||
"fieldtype": "data",
|
"fieldtype": "data",
|
||||||
"fieldname": "applicant_name",
|
"fieldname": "applicant_name",
|
||||||
"width": 120
|
"width": 130
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Application Status"),
|
"label": _("Application Status"),
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"fieldname": "application_status",
|
"fieldname": "application_status",
|
||||||
"width": 100
|
"width": 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": _("Job Offer"),
|
"label": _("Job Offer"),
|
||||||
|
@ -275,6 +275,11 @@ class TestLoan(unittest.TestCase):
|
|||||||
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
|
frappe.db.sql(""" UPDATE `tabLoan Security Price` SET loan_security_price = 250
|
||||||
where loan_security='Test Security 2'""")
|
where loan_security='Test Security 2'""")
|
||||||
|
|
||||||
|
create_process_loan_security_shortfall()
|
||||||
|
loan_security_shortfall = frappe.get_doc("Loan Security Shortfall", {"loan": loan.name})
|
||||||
|
self.assertEquals(loan_security_shortfall.status, "Completed")
|
||||||
|
self.assertEquals(loan_security_shortfall.shortfall_amount, 0)
|
||||||
|
|
||||||
def test_loan_security_unpledge(self):
|
def test_loan_security_unpledge(self):
|
||||||
pledge = [{
|
pledge = [{
|
||||||
"loan_security": "Test Security 1",
|
"loan_security": "Test Security 1",
|
||||||
|
@ -55,6 +55,9 @@ def check_for_ltv_shortfall(process_loan_security_shortfall):
|
|||||||
'total_interest_payable', 'disbursed_amount', 'status'],
|
'total_interest_payable', 'disbursed_amount', 'status'],
|
||||||
filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
|
filters={'status': ('in',['Disbursed','Partially Disbursed']), 'is_secured_loan': 1})
|
||||||
|
|
||||||
|
loan_shortfall_map = frappe._dict(frappe.get_all("Loan Security Shortfall",
|
||||||
|
fields=["loan", "name"], filters={"status": "Pending"}, as_list=1))
|
||||||
|
|
||||||
loan_security_map = {}
|
loan_security_map = {}
|
||||||
|
|
||||||
for loan in loans:
|
for loan in loans:
|
||||||
@ -71,14 +74,19 @@ def check_for_ltv_shortfall(process_loan_security_shortfall):
|
|||||||
for security, qty in pledged_securities.items():
|
for security, qty in pledged_securities.items():
|
||||||
if not ltv_ratio:
|
if not ltv_ratio:
|
||||||
ltv_ratio = get_ltv_ratio(security)
|
ltv_ratio = get_ltv_ratio(security)
|
||||||
security_value += loan_security_price_map.get(security) * qty
|
security_value += flt(loan_security_price_map.get(security)) * flt(qty)
|
||||||
|
|
||||||
current_ratio = (outstanding_amount/security_value) * 100
|
current_ratio = (outstanding_amount/security_value) * 100 if security_value else 0
|
||||||
|
|
||||||
if current_ratio > ltv_ratio:
|
if current_ratio > ltv_ratio:
|
||||||
shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
|
shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
|
||||||
create_loan_security_shortfall(loan.name, outstanding_amount, security_value, shortfall_amount,
|
create_loan_security_shortfall(loan.name, outstanding_amount, security_value, shortfall_amount,
|
||||||
process_loan_security_shortfall)
|
process_loan_security_shortfall)
|
||||||
|
elif loan_shortfall_map.get(loan.name):
|
||||||
|
shortfall_amount = outstanding_amount - ((security_value * ltv_ratio) / 100)
|
||||||
|
if shortfall_amount <= 0:
|
||||||
|
shortfall = loan_shortfall_map.get(loan.name)
|
||||||
|
update_pending_shortfall(shortfall)
|
||||||
|
|
||||||
def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
|
def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
|
||||||
existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
|
existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
|
||||||
@ -101,3 +109,11 @@ def get_ltv_ratio(loan_security):
|
|||||||
ltv_ratio = frappe.db.get_value('Loan Security Type', loan_security_type, 'loan_to_value_ratio')
|
ltv_ratio = frappe.db.get_value('Loan Security Type', loan_security_type, 'loan_to_value_ratio')
|
||||||
return ltv_ratio
|
return ltv_ratio
|
||||||
|
|
||||||
|
def update_pending_shortfall(shortfall):
|
||||||
|
# Get all pending loan security shortfall
|
||||||
|
frappe.db.set_value("Loan Security Shortfall", shortfall,
|
||||||
|
{
|
||||||
|
"status": "Completed",
|
||||||
|
"shortfall_amount": 0
|
||||||
|
})
|
||||||
|
|
||||||
|
@ -255,6 +255,9 @@ class JobCard(Document):
|
|||||||
data.actual_operation_time = time_in_mins
|
data.actual_operation_time = time_in_mins
|
||||||
data.actual_start_time = time_data[0].start_time if time_data else None
|
data.actual_start_time = time_data[0].start_time if time_data else None
|
||||||
data.actual_end_time = time_data[0].end_time if time_data else None
|
data.actual_end_time = time_data[0].end_time if time_data else None
|
||||||
|
if data.get("workstation") != self.workstation:
|
||||||
|
# workstations can change in a job card
|
||||||
|
data.workstation = self.workstation
|
||||||
|
|
||||||
wo.flags.ignore_validate_update_after_submit = True
|
wo.flags.ignore_validate_update_after_submit = True
|
||||||
wo.update_operation_status()
|
wo.update_operation_status()
|
||||||
|
@ -11,10 +11,14 @@
|
|||||||
"from_warehouse",
|
"from_warehouse",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
|
"required_bom_qty",
|
||||||
"quantity",
|
"quantity",
|
||||||
"uom",
|
"uom",
|
||||||
"projected_qty",
|
"projected_qty",
|
||||||
"actual_qty",
|
"actual_qty",
|
||||||
|
"ordered_qty",
|
||||||
|
"reserved_qty_for_production",
|
||||||
|
"safety_stock",
|
||||||
"item_details",
|
"item_details",
|
||||||
"description",
|
"description",
|
||||||
"min_order_qty",
|
"min_order_qty",
|
||||||
@ -129,11 +133,40 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "From Warehouse",
|
"label": "From Warehouse",
|
||||||
"options": "Warehouse"
|
"options": "Warehouse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fetch_from": "item_code.safety_stock",
|
||||||
|
"fieldname": "safety_stock",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Safety Stock",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "ordered_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Ordered Qty",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "reserved_qty_for_production",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Reserved Qty for Production",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "required_bom_qty",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"label": "Required Qty as per BOM",
|
||||||
|
"no_copy": 1,
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-02-03 12:22:29.913302",
|
"modified": "2021-03-26 12:41:13.013149",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Material Request Plan Item",
|
"name": "Material Request Plan Item",
|
||||||
|
@ -251,7 +251,8 @@ frappe.ui.form.on('Production Plan', {
|
|||||||
|
|
||||||
get_items_for_material_requests: function(frm, warehouses) {
|
get_items_for_material_requests: function(frm, warehouses) {
|
||||||
const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom', 'from_warehouse',
|
const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom', 'from_warehouse',
|
||||||
'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
|
'min_order_qty', 'required_bom_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'ordered_qty',
|
||||||
|
'reserved_qty_for_production', 'material_request_type'];
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
|
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"material_request_planning",
|
"material_request_planning",
|
||||||
"include_non_stock_items",
|
"include_non_stock_items",
|
||||||
"include_subcontracted_items",
|
"include_subcontracted_items",
|
||||||
|
"include_safety_stock",
|
||||||
"ignore_existing_ordered_qty",
|
"ignore_existing_ordered_qty",
|
||||||
"column_break_25",
|
"column_break_25",
|
||||||
"for_warehouse",
|
"for_warehouse",
|
||||||
@ -309,13 +310,19 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Sales Order Status",
|
"label": "Sales Order Status",
|
||||||
"options": "\nTo Deliver and Bill\nTo Bill\nTo Deliver"
|
"options": "\nTo Deliver and Bill\nTo Bill\nTo Deliver"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "include_safety_stock",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Include Safety Stock in Required Qty Calculation"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-calendar",
|
"icon": "fa fa-calendar",
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-11-10 18:01:54.991970",
|
"modified": "2021-03-08 11:17:25.470147",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Production Plan",
|
"name": "Production Plan",
|
||||||
|
@ -434,12 +434,14 @@ def download_raw_materials(doc):
|
|||||||
if isinstance(doc, string_types):
|
if isinstance(doc, string_types):
|
||||||
doc = frappe._dict(json.loads(doc))
|
doc = frappe._dict(json.loads(doc))
|
||||||
|
|
||||||
item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
|
item_list = [['Item Code', 'Description', 'Stock UOM', 'Warehouse', 'Required Qty as per BOM',
|
||||||
'projected Qty', 'Actual Qty']]
|
'Projected Qty', 'Actual Qty', 'Ordered Qty', 'Reserved Qty for Production',
|
||||||
|
'Safety Stock', 'Required Qty']]
|
||||||
|
|
||||||
for d in get_items_for_material_requests(doc):
|
for d in get_items_for_material_requests(doc):
|
||||||
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('quantity'),
|
item_list.append([d.get('item_code'), d.get('description'), d.get('stock_uom'), d.get('warehouse'),
|
||||||
d.get('warehouse'), d.get('projected_qty'), d.get('actual_qty')])
|
d.get('required_bom_qty'), d.get('projected_qty'), d.get('actual_qty'), d.get('ordered_qty'),
|
||||||
|
d.get('reserved_qty_for_production'), d.get('safety_stock'), d.get('quantity')])
|
||||||
|
|
||||||
if not doc.get('for_warehouse'):
|
if not doc.get('for_warehouse'):
|
||||||
row = {'item_code': d.get('item_code')}
|
row = {'item_code': d.get('item_code')}
|
||||||
@ -447,8 +449,9 @@ def download_raw_materials(doc):
|
|||||||
if d.get("warehouse") == bin_dict.get('warehouse'):
|
if d.get("warehouse") == bin_dict.get('warehouse'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
item_list.append(['', '', '', '', bin_dict.get('warehouse'),
|
item_list.append(['', '', '', bin_dict.get('warehouse'), '',
|
||||||
bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0)])
|
bin_dict.get('projected_qty', 0), bin_dict.get('actual_qty', 0),
|
||||||
|
bin_dict.get('ordered_qty', 0), bin_dict.get('reserved_qty_for_production', 0)])
|
||||||
|
|
||||||
build_csv_response(item_list, doc.name)
|
build_csv_response(item_list, doc.name)
|
||||||
|
|
||||||
@ -482,7 +485,7 @@ def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_ite
|
|||||||
ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(planned_qty)s, 0) as qty,
|
ifnull(%(parent_qty)s * sum(bom_item.stock_qty/ifnull(bom.quantity, 1)) * %(planned_qty)s, 0) as qty,
|
||||||
item.is_sub_contracted_item as is_sub_contracted, bom_item.source_warehouse,
|
item.is_sub_contracted_item as is_sub_contracted, bom_item.source_warehouse,
|
||||||
item.default_bom as default_bom, bom_item.description as description,
|
item.default_bom as default_bom, bom_item.description as description,
|
||||||
bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty,
|
bom_item.stock_uom as stock_uom, item.min_order_qty as min_order_qty, item.safety_stock as safety_stock,
|
||||||
item_default.default_warehouse, item.purchase_uom, item_uom.conversion_factor
|
item_default.default_warehouse, item.purchase_uom, item_uom.conversion_factor
|
||||||
FROM
|
FROM
|
||||||
`tabBOM Item` bom_item
|
`tabBOM Item` bom_item
|
||||||
@ -518,8 +521,8 @@ def get_subitems(doc, data, item_details, bom_no, company, include_non_stock_ite
|
|||||||
include_non_stock_items, include_subcontracted_items, d.qty)
|
include_non_stock_items, include_subcontracted_items, d.qty)
|
||||||
return item_details
|
return item_details
|
||||||
|
|
||||||
def get_material_request_items(row, sales_order,
|
def get_material_request_items(row, sales_order, company,
|
||||||
company, ignore_existing_ordered_qty, warehouse, bin_dict):
|
ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict):
|
||||||
total_qty = row['qty']
|
total_qty = row['qty']
|
||||||
|
|
||||||
required_qty = 0
|
required_qty = 0
|
||||||
@ -543,17 +546,24 @@ def get_material_request_items(row, sales_order,
|
|||||||
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
|
if frappe.db.get_value("UOM", row['purchase_uom'], "must_be_whole_number"):
|
||||||
required_qty = ceil(required_qty)
|
required_qty = ceil(required_qty)
|
||||||
|
|
||||||
|
if include_safety_stock:
|
||||||
|
required_qty += flt(row['safety_stock'])
|
||||||
|
|
||||||
if required_qty > 0:
|
if required_qty > 0:
|
||||||
return {
|
return {
|
||||||
'item_code': row.item_code,
|
'item_code': row.item_code,
|
||||||
'item_name': row.item_name,
|
'item_name': row.item_name,
|
||||||
'quantity': required_qty,
|
'quantity': required_qty,
|
||||||
|
'required_bom_qty': total_qty,
|
||||||
'description': row.description,
|
'description': row.description,
|
||||||
'stock_uom': row.get("stock_uom"),
|
'stock_uom': row.get("stock_uom"),
|
||||||
'warehouse': warehouse or row.get('source_warehouse') \
|
'warehouse': warehouse or row.get('source_warehouse') \
|
||||||
or row.get('default_warehouse') or item_group_defaults.get("default_warehouse"),
|
or row.get('default_warehouse') or item_group_defaults.get("default_warehouse"),
|
||||||
|
'safety_stock': row.safety_stock,
|
||||||
'actual_qty': bin_dict.get("actual_qty", 0),
|
'actual_qty': bin_dict.get("actual_qty", 0),
|
||||||
'projected_qty': bin_dict.get("projected_qty", 0),
|
'projected_qty': bin_dict.get("projected_qty", 0),
|
||||||
|
'ordered_qty': bin_dict.get("ordered_qty", 0),
|
||||||
|
'reserved_qty_for_production': bin_dict.get("reserved_qty_for_production", 0),
|
||||||
'min_order_qty': row['min_order_qty'],
|
'min_order_qty': row['min_order_qty'],
|
||||||
'material_request_type': row.get("default_material_request_type"),
|
'material_request_type': row.get("default_material_request_type"),
|
||||||
'sales_order': sales_order,
|
'sales_order': sales_order,
|
||||||
@ -620,7 +630,8 @@ def get_bin_details(row, company, for_warehouse=None, all_warehouse=False):
|
|||||||
""".format(lft, rgt, company)
|
""".format(lft, rgt, company)
|
||||||
|
|
||||||
return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
|
return frappe.db.sql(""" select ifnull(sum(projected_qty),0) as projected_qty,
|
||||||
ifnull(sum(actual_qty),0) as actual_qty, warehouse from `tabBin`
|
ifnull(sum(actual_qty),0) as actual_qty, ifnull(sum(ordered_qty),0) as ordered_qty,
|
||||||
|
ifnull(sum(reserved_qty_for_production),0) as reserved_qty_for_production, warehouse from `tabBin`
|
||||||
where item_code = %(item_code)s {conditions}
|
where item_code = %(item_code)s {conditions}
|
||||||
group by item_code, warehouse
|
group by item_code, warehouse
|
||||||
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
|
""".format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1)
|
||||||
@ -660,6 +671,7 @@ def get_items_for_material_requests(doc, warehouses=None):
|
|||||||
|
|
||||||
company = doc.get('company')
|
company = doc.get('company')
|
||||||
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
|
ignore_existing_ordered_qty = doc.get('ignore_existing_ordered_qty')
|
||||||
|
include_safety_stock = doc.get('include_safety_stock')
|
||||||
|
|
||||||
so_item_details = frappe._dict()
|
so_item_details = frappe._dict()
|
||||||
for data in po_items:
|
for data in po_items:
|
||||||
@ -711,6 +723,7 @@ def get_items_for_material_requests(doc, warehouses=None):
|
|||||||
'description' : item_master.description,
|
'description' : item_master.description,
|
||||||
'stock_uom' : item_master.stock_uom,
|
'stock_uom' : item_master.stock_uom,
|
||||||
'conversion_factor' : conversion_factor,
|
'conversion_factor' : conversion_factor,
|
||||||
|
'safety_stock': item_master.safety_stock
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -732,7 +745,7 @@ def get_items_for_material_requests(doc, warehouses=None):
|
|||||||
|
|
||||||
if details.qty > 0:
|
if details.qty > 0:
|
||||||
items = get_material_request_items(details, sales_order, company,
|
items = get_material_request_items(details, sales_order, company,
|
||||||
ignore_existing_ordered_qty, warehouse, bin_dict)
|
ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict)
|
||||||
if items:
|
if items:
|
||||||
mr_items.append(items)
|
mr_items.append(items)
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class TestWorkOrder(unittest.TestCase):
|
|||||||
wo_order.set_work_order_operations()
|
wo_order.set_work_order_operations()
|
||||||
self.assertEqual(wo_order.planned_operating_cost, cost*2)
|
self.assertEqual(wo_order.planned_operating_cost, cost*2)
|
||||||
|
|
||||||
def test_resered_qty_for_partial_completion(self):
|
def test_reserved_qty_for_partial_completion(self):
|
||||||
item = "_Test Item"
|
item = "_Test Item"
|
||||||
warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
|
warehouse = create_warehouse("Test Warehouse for reserved_qty - _TC")
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ class TestWorkOrder(unittest.TestCase):
|
|||||||
s.submit()
|
s.submit()
|
||||||
|
|
||||||
bin1_at_completion = get_bin(item, warehouse)
|
bin1_at_completion = get_bin(item, warehouse)
|
||||||
|
|
||||||
self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
|
self.assertEqual(cint(bin1_at_completion.reserved_qty_for_production),
|
||||||
reserved_qty_on_submission - 1)
|
reserved_qty_on_submission - 1)
|
||||||
|
|
||||||
@ -592,6 +592,55 @@ class TestWorkOrder(unittest.TestCase):
|
|||||||
|
|
||||||
frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
|
frappe.db.set_value("Manufacturing Settings", None, "backflush_raw_materials_based_on", "BOM")
|
||||||
|
|
||||||
|
def test_make_stock_entry_for_customer_provided_item(self):
|
||||||
|
finished_item = 'Test Item for Make Stock Entry 1'
|
||||||
|
make_item(finished_item, {
|
||||||
|
"include_item_in_manufacturing": 1,
|
||||||
|
"is_stock_item": 1
|
||||||
|
})
|
||||||
|
|
||||||
|
customer_provided_item = 'CUST-0987'
|
||||||
|
make_item(customer_provided_item, {
|
||||||
|
'is_purchase_item': 0,
|
||||||
|
'is_customer_provided_item': 1,
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"include_item_in_manufacturing": 1,
|
||||||
|
'customer': '_Test Customer'
|
||||||
|
})
|
||||||
|
|
||||||
|
if not frappe.db.exists('BOM', {'item': finished_item}):
|
||||||
|
make_bom(item=finished_item, raw_materials=[customer_provided_item], rm_qty=1)
|
||||||
|
|
||||||
|
company = "_Test Company with perpetual inventory"
|
||||||
|
customer_warehouse = create_warehouse("Test Customer Provided Warehouse", company=company)
|
||||||
|
wo = make_wo_order_test_record(item=finished_item, qty=1, source_warehouse=customer_warehouse,
|
||||||
|
company=company)
|
||||||
|
|
||||||
|
ste = frappe.get_doc(make_stock_entry(wo.name, purpose='Material Transfer for Manufacture'))
|
||||||
|
ste.insert()
|
||||||
|
|
||||||
|
self.assertEqual(len(ste.items), 1)
|
||||||
|
for item in ste.items:
|
||||||
|
self.assertEqual(item.allow_zero_valuation_rate, 1)
|
||||||
|
self.assertEqual(item.valuation_rate, 0)
|
||||||
|
|
||||||
|
def test_valuation_rate_missing_on_make_stock_entry(self):
|
||||||
|
item_name = 'Test Valuation Rate Missing'
|
||||||
|
make_item(item_name, {
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"include_item_in_manufacturing": 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
if not frappe.db.get_value('BOM', {'item': item_name}):
|
||||||
|
make_bom(item=item_name, raw_materials=[item_name], rm_qty=1)
|
||||||
|
|
||||||
|
company = "_Test Company with perpetual inventory"
|
||||||
|
source_warehouse = create_warehouse("Test Valuation Rate Missing Warehouse", company=company)
|
||||||
|
wo = make_wo_order_test_record(item=item_name, qty=1, source_warehouse=source_warehouse,
|
||||||
|
company=company)
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, make_stock_entry, wo.name, 'Material Transfer for Manufacture')
|
||||||
|
|
||||||
def get_scrap_item_details(bom_no):
|
def get_scrap_item_details(bom_no):
|
||||||
scrap_items = {}
|
scrap_items = {}
|
||||||
for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
|
for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item`
|
||||||
@ -609,6 +658,15 @@ def allow_overproduction(fieldname, percentage):
|
|||||||
|
|
||||||
def make_wo_order_test_record(**args):
|
def make_wo_order_test_record(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
|
if args.company and args.company != "_Test Company":
|
||||||
|
warehouse_map = {
|
||||||
|
"fg_warehouse": "_Test FG Warehouse",
|
||||||
|
"wip_warehouse": "_Test WIP Warehouse"
|
||||||
|
}
|
||||||
|
|
||||||
|
for attr, wh_name in warehouse_map.items():
|
||||||
|
if not args.get(attr):
|
||||||
|
args[attr] = create_warehouse(wh_name, company=args.company)
|
||||||
|
|
||||||
wo_order = frappe.new_doc("Work Order")
|
wo_order = frappe.new_doc("Work Order")
|
||||||
wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
|
wo_order.production_item = args.production_item or args.item or args.item_code or "_Test FG Item"
|
||||||
|
@ -333,8 +333,7 @@
|
|||||||
"fieldname": "operations",
|
"fieldname": "operations",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Operations",
|
"label": "Operations",
|
||||||
"options": "Work Order Operation",
|
"options": "Work Order Operation"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "operations",
|
"depends_on": "operations",
|
||||||
@ -496,7 +495,7 @@
|
|||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-05 19:32:43.323054",
|
"modified": "2021-03-16 13:27:51.116484",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "Work Order",
|
"name": "Work Order",
|
||||||
|
@ -61,7 +61,7 @@ class ForecastingReport(ExponentialSmoothingForecast):
|
|||||||
|
|
||||||
from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
|
from_date = add_years(self.filters.from_date, cint(self.filters.no_of_years) * -1)
|
||||||
self.period_list = get_period_list(from_date, self.filters.to_date,
|
self.period_list = get_period_list(from_date, self.filters.to_date,
|
||||||
from_date, self.filters.to_date, None, self.filters.periodicity, ignore_fiscal_year=True)
|
from_date, self.filters.to_date, "Date Range", self.filters.periodicity, ignore_fiscal_year=True)
|
||||||
|
|
||||||
order_data = self.get_data_for_forecast() or []
|
order_data = self.get_data_for_forecast() or []
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class Donation(Document):
|
|||||||
self.load_from_db()
|
self.load_from_db()
|
||||||
self.create_payment_entry()
|
self.create_payment_entry()
|
||||||
|
|
||||||
def create_payment_entry(self):
|
def create_payment_entry(self, date=None):
|
||||||
settings = frappe.get_doc('Non Profit Settings')
|
settings = frappe.get_doc('Non Profit Settings')
|
||||||
if not settings.automate_donation_payment_entries:
|
if not settings.automate_donation_payment_entries:
|
||||||
return
|
return
|
||||||
@ -58,8 +58,9 @@ class Donation(Document):
|
|||||||
frappe.flags.ignore_account_permission = False
|
frappe.flags.ignore_account_permission = False
|
||||||
pe.paid_from = settings.donation_debit_account
|
pe.paid_from = settings.donation_debit_account
|
||||||
pe.paid_to = settings.donation_payment_account
|
pe.paid_to = settings.donation_payment_account
|
||||||
|
pe.posting_date = date or getdate()
|
||||||
pe.reference_no = self.name
|
pe.reference_no = self.name
|
||||||
pe.reference_date = getdate()
|
pe.reference_date = date or getdate()
|
||||||
pe.flags.ignore_mandatory = True
|
pe.flags.ignore_mandatory = True
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
@ -91,6 +92,10 @@ def capture_razorpay_donations(*args, **kwargs):
|
|||||||
if not data.event == 'payment.captured':
|
if not data.event == 'payment.captured':
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# to avoid capturing subscription payments as donations
|
||||||
|
if payment.description and 'subscription' in str(payment.description).lower():
|
||||||
|
return
|
||||||
|
|
||||||
donor = get_donor(payment.email)
|
donor = get_donor(payment.email)
|
||||||
if not donor:
|
if not donor:
|
||||||
donor = create_donor(payment)
|
donor = create_donor(payment)
|
||||||
@ -119,7 +124,7 @@ def create_donation(donor, payment):
|
|||||||
'donor_name': donor.donor_name,
|
'donor_name': donor.donor_name,
|
||||||
'email': donor.email,
|
'email': donor.email,
|
||||||
'date': getdate(),
|
'date': getdate(),
|
||||||
'amount': flt(payment.amount),
|
'amount': flt(payment.amount) / 100, # Convert to rupees from paise
|
||||||
'mode_of_payment': payment.method,
|
'mode_of_payment': payment.method,
|
||||||
'razorpay_payment_id': payment.id
|
'razorpay_payment_id': payment.id
|
||||||
}).insert(ignore_mandatory=True)
|
}).insert(ignore_mandatory=True)
|
||||||
|
@ -48,7 +48,7 @@ class Membership(Document):
|
|||||||
last_membership = erpnext.get_last_membership(self.member)
|
last_membership = erpnext.get_last_membership(self.member)
|
||||||
|
|
||||||
# if person applied for offline membership
|
# if person applied for offline membership
|
||||||
if last_membership and not frappe.session.user == "Administrator":
|
if last_membership and last_membership.name != self.name and not frappe.session.user == "Administrator":
|
||||||
# if last membership does not expire in 30 days, then do not allow to renew
|
# if last membership does not expire in 30 days, then do not allow to renew
|
||||||
if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
|
if getdate(add_days(last_membership.to_date, -30)) > getdate(nowdate()) :
|
||||||
frappe.throw(_("You can only renew if your membership expires within 30 days"))
|
frappe.throw(_("You can only renew if your membership expires within 30 days"))
|
||||||
@ -90,6 +90,7 @@ class Membership(Document):
|
|||||||
self.validate_membership_type_and_settings(plan, settings)
|
self.validate_membership_type_and_settings(plan, settings)
|
||||||
|
|
||||||
invoice = make_invoice(self, member, plan, settings)
|
invoice = make_invoice(self, member, plan, settings)
|
||||||
|
self.reload()
|
||||||
self.invoice = invoice.name
|
self.invoice = invoice.name
|
||||||
|
|
||||||
if with_payment_entry:
|
if with_payment_entry:
|
||||||
@ -284,10 +285,11 @@ def trigger_razorpay_subscription(*args, **kwargs):
|
|||||||
|
|
||||||
settings = frappe.get_doc("Non Profit Settings")
|
settings = frappe.get_doc("Non Profit Settings")
|
||||||
if settings.allow_invoicing and settings.automate_membership_invoicing:
|
if settings.allow_invoicing and settings.automate_membership_invoicing:
|
||||||
|
membership.reload()
|
||||||
membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
|
membership.generate_invoice(with_payment_entry=settings.automate_membership_payment_entries, save=True)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), __("Payment ID"), payment.id)
|
message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id)
|
||||||
log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
|
log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name))
|
||||||
notify_failure(log)
|
notify_failure(log)
|
||||||
return { "status": "Failed", "reason": e}
|
return { "status": "Failed", "reason": e}
|
||||||
|
@ -19,7 +19,7 @@ frappe.ui.form.on("Non Profit Settings", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
frm.set_query("debit_account", function() {
|
frm.set_query("membership_debit_account", function() {
|
||||||
return {
|
return {
|
||||||
filters: {
|
filters: {
|
||||||
"account_type": "Receivable",
|
"account_type": "Receivable",
|
||||||
@ -29,6 +29,16 @@ frappe.ui.form.on("Non Profit Settings", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("donation_debit_account", function() {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"account_type": "Receivable",
|
||||||
|
"is_group": 0,
|
||||||
|
"company": frm.doc.donation_company
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
frm.set_query("membership_payment_account", function () {
|
frm.set_query("membership_payment_account", function () {
|
||||||
var account_types = ["Bank", "Cash"];
|
var account_types = ["Bank", "Cash"];
|
||||||
return {
|
return {
|
||||||
@ -40,6 +50,17 @@ frappe.ui.form.on("Non Profit Settings", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frm.set_query("donation_payment_account", function () {
|
||||||
|
var account_types = ["Bank", "Cash"];
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
"account_type": ["in", account_types],
|
||||||
|
"is_group": 0,
|
||||||
|
"company": frm.doc.donation_company
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership";
|
let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership";
|
||||||
|
|
||||||
frm.set_intro(__("You can learn more about memberships in the manual. ") + `<a href='${docs_url}'>${__('ERPNext Docs')}</a>`, true);
|
frm.set_intro(__("You can learn more about memberships in the manual. ") + `<a href='${docs_url}'>${__('ERPNext Docs')}</a>`, true);
|
||||||
|
@ -720,7 +720,7 @@ erpnext.patches.v13_0.delete_report_requested_items_to_order
|
|||||||
erpnext.patches.v12_0.update_item_tax_template_company
|
erpnext.patches.v12_0.update_item_tax_template_company
|
||||||
erpnext.patches.v13_0.move_branch_code_to_bank_account
|
erpnext.patches.v13_0.move_branch_code_to_bank_account
|
||||||
erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
|
erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
|
||||||
erpnext.patches.v13_0.add_standard_navbar_items #4
|
erpnext.patches.v13_0.add_standard_navbar_items #2021-03-24
|
||||||
erpnext.patches.v13_0.stock_entry_enhancements
|
erpnext.patches.v13_0.stock_entry_enhancements
|
||||||
erpnext.patches.v12_0.update_state_code_for_daman_and_diu
|
erpnext.patches.v12_0.update_state_code_for_daman_and_diu
|
||||||
erpnext.patches.v12_0.rename_lost_reason_detail
|
erpnext.patches.v12_0.rename_lost_reason_detail
|
||||||
@ -759,3 +759,6 @@ erpnext.patches.v13_0.update_vehicle_no_reqd_condition
|
|||||||
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
|
||||||
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
|
||||||
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
|
||||||
|
erpnext.patches.v13_0.setup_uae_vat_fields
|
||||||
|
execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
||||||
|
erpnext.patches.v13_0.rename_discharge_date_in_ip_record
|
||||||
|
@ -11,6 +11,7 @@ def execute():
|
|||||||
|
|
||||||
# Update options in gst_state custom fields
|
# Update options in gst_state custom fields
|
||||||
for field in custom_fields:
|
for field in custom_fields:
|
||||||
gst_state_field = frappe.get_doc('Custom Field', field)
|
if frappe.db.exists('Custom Field', field):
|
||||||
gst_state_field.options = '\n'.join(states)
|
gst_state_field = frappe.get_doc('Custom Field', field)
|
||||||
gst_state_field.save()
|
gst_state_field.options = '\n'.join(states)
|
||||||
|
gst_state_field.save()
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.utils.rename_field import rename_field
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
frappe.reload_doc("Healthcare", "doctype", "Inpatient Record")
|
||||||
|
if frappe.db.has_column("Inpatient Record", "discharge_date"):
|
||||||
|
rename_field("Inpatient Record", "discharge_date", "discharge_datetime")
|
@ -2,15 +2,12 @@ import frappe
|
|||||||
from erpnext.regional.india.setup import make_custom_fields
|
from erpnext.regional.india.setup import make_custom_fields
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
if frappe.get_all('Company', filters = {'country': 'India'}):
|
||||||
if not company:
|
make_custom_fields()
|
||||||
return
|
|
||||||
|
|
||||||
make_custom_fields()
|
if not frappe.db.exists('Party Type', 'Donor'):
|
||||||
|
frappe.get_doc({
|
||||||
if not frappe.db.exists('Party Type', 'Donor'):
|
'doctype': 'Party Type',
|
||||||
frappe.get_doc({
|
'party_type': 'Donor',
|
||||||
'doctype': 'Party Type',
|
'account_type': 'Receivable'
|
||||||
'party_type': 'Donor',
|
}).insert(ignore_permissions=True)
|
||||||
'account_type': 'Receivable'
|
|
||||||
}).insert(ignore_permissions=True)
|
|
||||||
|
12
erpnext/patches/v13_0/setup_uae_vat_fields.py
Normal file
12
erpnext/patches/v13_0/setup_uae_vat_fields.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) 2019, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
from erpnext.regional.united_arab_emirates.setup import setup
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
company = frappe.get_all('Company', filters = {'country': 'United Arab Emirates'})
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
setup()
|
@ -9,17 +9,10 @@ from frappe import _, bold
|
|||||||
from frappe.utils import getdate, date_diff, comma_and, formatdate
|
from frappe.utils import getdate, date_diff, comma_and, formatdate
|
||||||
|
|
||||||
class AdditionalSalary(Document):
|
class AdditionalSalary(Document):
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
if self.ref_doctype == "Employee Advance" and self.ref_docname:
|
if self.ref_doctype == "Employee Advance" and self.ref_docname:
|
||||||
frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
|
frappe.db.set_value("Employee Advance", self.ref_docname, "return_amount", self.amount)
|
||||||
|
|
||||||
def before_insert(self):
|
|
||||||
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
|
||||||
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company, "docstatus": 1}):
|
|
||||||
|
|
||||||
frappe.throw(_("Additional Salary Component Exists."))
|
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.validate_salary_structure()
|
self.validate_salary_structure()
|
||||||
@ -89,10 +82,11 @@ class AdditionalSalary(Document):
|
|||||||
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1
|
||||||
return amount_per_day * no_of_days
|
return amount_per_day * no_of_days
|
||||||
|
|
||||||
@frappe.whitelist()
|
def get_additional_salaries(employee, start_date, end_date, component_type):
|
||||||
def get_additional_salary_component(employee, start_date, end_date, component_type):
|
additional_salary_list = frappe.db.sql("""
|
||||||
additional_salaries = frappe.db.sql("""
|
select name, salary_component as component, type, amount,
|
||||||
select name, salary_component, type, amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
|
overwrite_salary_structure_amount as overwrite,
|
||||||
|
deduct_full_tax_on_selected_payroll_date
|
||||||
from `tabAdditional Salary`
|
from `tabAdditional Salary`
|
||||||
where employee=%(employee)s
|
where employee=%(employee)s
|
||||||
and docstatus = 1
|
and docstatus = 1
|
||||||
@ -102,7 +96,7 @@ def get_additional_salary_component(employee, start_date, end_date, component_ty
|
|||||||
from_date <= %(to_date)s and to_date >= %(to_date)s
|
from_date <= %(to_date)s and to_date >= %(to_date)s
|
||||||
)
|
)
|
||||||
and type = %(component_type)s
|
and type = %(component_type)s
|
||||||
order by salary_component, overwrite_salary_structure_amount DESC
|
order by salary_component, overwrite ASC
|
||||||
""", {
|
""", {
|
||||||
'employee': employee,
|
'employee': employee,
|
||||||
'from_date': start_date,
|
'from_date': start_date,
|
||||||
@ -110,38 +104,18 @@ def get_additional_salary_component(employee, start_date, end_date, component_ty
|
|||||||
'component_type': "Earning" if component_type == "earnings" else "Deduction"
|
'component_type': "Earning" if component_type == "earnings" else "Deduction"
|
||||||
}, as_dict=1)
|
}, as_dict=1)
|
||||||
|
|
||||||
existing_salary_components= []
|
additional_salaries = []
|
||||||
salary_components_details = {}
|
components_to_overwrite = []
|
||||||
additional_salary_details = []
|
|
||||||
|
|
||||||
overwrites_components = [ele.salary_component for ele in additional_salaries if ele.overwrite_salary_structure_amount == 1]
|
for d in additional_salary_list:
|
||||||
|
if d.overwrite:
|
||||||
|
if d.component in components_to_overwrite:
|
||||||
|
frappe.throw(_("Multiple Additional Salaries with overwrite "
|
||||||
|
"property exist for Salary Component {0} between {1} and {2}.").format(
|
||||||
|
frappe.bold(d.component), start_date, end_date), title=_("Error"))
|
||||||
|
|
||||||
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
|
components_to_overwrite.append(d.component)
|
||||||
for d in additional_salaries:
|
|
||||||
|
|
||||||
if d.salary_component not in existing_salary_components:
|
additional_salaries.append(d)
|
||||||
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
|
|
||||||
struct_row = frappe._dict({'salary_component': d.salary_component})
|
|
||||||
if component:
|
|
||||||
struct_row.update(component[0])
|
|
||||||
|
|
||||||
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
|
return additional_salaries
|
||||||
struct_row['is_additional_component'] = 1
|
|
||||||
|
|
||||||
salary_components_details[d.salary_component] = struct_row
|
|
||||||
|
|
||||||
|
|
||||||
if overwrites_components.count(d.salary_component) > 1:
|
|
||||||
frappe.throw(_("Multiple Additional Salaries with overwrite property exist for Salary Component: {0} between {1} and {2}.".format(d.salary_component, start_date, end_date)), title=_("Error"))
|
|
||||||
else:
|
|
||||||
additional_salary_details.append({
|
|
||||||
'name': d.name,
|
|
||||||
'component': d.salary_component,
|
|
||||||
'amount': d.amount,
|
|
||||||
'type': d.type,
|
|
||||||
'overwrite': d.overwrite_salary_structure_amount,
|
|
||||||
})
|
|
||||||
|
|
||||||
existing_salary_components.append(d.salary_component)
|
|
||||||
|
|
||||||
return salary_components_details, additional_salary_details
|
|
||||||
|
@ -15,9 +15,12 @@ from frappe.utils import getdate, add_days, get_datetime, flt
|
|||||||
|
|
||||||
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
|
test_dependencies = ["Salary Component", "Salary Slip", "Account"]
|
||||||
class TestGratuity(unittest.TestCase):
|
class TestGratuity(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
make_earning_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
||||||
make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
make_deduction_salary_component(setup=True, test_tax=True, company_list=['_Test Company'])
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
frappe.db.sql("DELETE FROM `tabGratuity`")
|
frappe.db.sql("DELETE FROM `tabGratuity`")
|
||||||
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
|
frappe.db.sql("DELETE FROM `tabAdditional Salary` WHERE ref_doctype = 'Gratuity'")
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee.test_employee import make_employee
|
|||||||
from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
|
||||||
make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip
|
make_earning_salary_component, make_deduction_salary_component, create_account, make_employee_salary_slip
|
||||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure, create_salary_structure_assignment
|
||||||
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
|
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry, create_loan_type, create_loan_accounts
|
||||||
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
|
||||||
|
|
||||||
class TestPayrollEntry(unittest.TestCase):
|
class TestPayrollEntry(unittest.TestCase):
|
||||||
@ -168,15 +168,23 @@ class TestPayrollEntry(unittest.TestCase):
|
|||||||
salary_structure = "Test Salary Structure for Loan"
|
salary_structure = "Test Salary Structure for Loan"
|
||||||
make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency)
|
make_salary_structure(salary_structure, "Monthly", employee=employee_doc.name, company="_Test Company", currency=company_doc.default_currency)
|
||||||
|
|
||||||
|
if not frappe.db.exists("Loan Type", "Car Loan"):
|
||||||
|
create_loan_accounts()
|
||||||
|
create_loan_type("Car Loan", 500000, 8.4,
|
||||||
|
is_term_loan=1,
|
||||||
|
mode_of_payment='Cash',
|
||||||
|
payment_account='Payment Account - _TC',
|
||||||
|
loan_account='Loan Account - _TC',
|
||||||
|
interest_income_account='Interest Income Account - _TC',
|
||||||
|
penalty_income_account='Penalty Income Account - _TC')
|
||||||
|
|
||||||
loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
|
loan = create_loan(applicant, "Car Loan", 280000, "Repay Over Number of Periods", 20, posting_date=add_months(nowdate(), -1))
|
||||||
loan.repay_from_salary = 1
|
loan.repay_from_salary = 1
|
||||||
loan.submit()
|
loan.submit()
|
||||||
|
|
||||||
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
|
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=add_months(nowdate(), -1))
|
||||||
|
|
||||||
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
|
process_loan_interest_accrual_for_term_loans(posting_date=nowdate())
|
||||||
|
|
||||||
|
|
||||||
dates = get_start_end_dates('Monthly', nowdate())
|
dates = get_start_end_dates('Monthly', nowdate())
|
||||||
make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account,
|
make_payroll_entry(company="_Test Company", start_date=dates.start_date, payable_account=company_doc.default_payroll_payable_account,
|
||||||
currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
|
currency=company_doc.default_currency, end_date=dates.end_date, branch=branch, cost_center="Main - _TC", payment_account="Cash - _TC")
|
||||||
@ -267,4 +275,4 @@ def get_salary_slip(user, period, salary_structure):
|
|||||||
salary_slip.calculate_net_pay()
|
salary_slip.calculate_net_pay()
|
||||||
salary_slip.db_update()
|
salary_slip.db_update()
|
||||||
|
|
||||||
return salary_slip
|
return salary_slip
|
||||||
|
@ -74,43 +74,46 @@ frappe.ui.form.on("Salary Slip", {
|
|||||||
if (!frm.doc.letter_head && company.default_letter_head) {
|
if (!frm.doc.letter_head && company.default_letter_head) {
|
||||||
frm.set_value('letter_head', company.default_letter_head);
|
frm.set_value('letter_head', company.default_letter_head);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
currency: function(frm) {
|
||||||
frm.trigger("set_dynamic_labels");
|
frm.trigger("set_dynamic_labels");
|
||||||
},
|
},
|
||||||
|
|
||||||
set_dynamic_labels: function(frm) {
|
set_dynamic_labels: function(frm) {
|
||||||
var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency");
|
var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency");
|
||||||
frappe.run_serially([
|
if (frm.doc.employee && frm.doc.currency) {
|
||||||
() => frm.events.set_exchange_rate(frm, company_currency),
|
frappe.run_serially([
|
||||||
() => frm.events.change_form_labels(frm, company_currency),
|
() => frm.events.set_exchange_rate(frm, company_currency),
|
||||||
() => frm.events.change_grid_labels(frm),
|
() => frm.events.change_form_labels(frm, company_currency),
|
||||||
() => frm.refresh_fields()
|
() => frm.events.change_grid_labels(frm),
|
||||||
]);
|
() => frm.refresh_fields()
|
||||||
|
]);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
set_exchange_rate: function(frm, company_currency) {
|
set_exchange_rate: function(frm, company_currency) {
|
||||||
if (frm.doc.docstatus === 0) {
|
if (frm.doc.currency) {
|
||||||
if (frm.doc.currency) {
|
var from_currency = frm.doc.currency;
|
||||||
var from_currency = frm.doc.currency;
|
if (from_currency != company_currency) {
|
||||||
if (from_currency != company_currency) {
|
frm.events.hide_loan_section(frm);
|
||||||
frm.events.hide_loan_section(frm);
|
frappe.call({
|
||||||
frappe.call({
|
method: "erpnext.setup.utils.get_exchange_rate",
|
||||||
method: "erpnext.setup.utils.get_exchange_rate",
|
args: {
|
||||||
args: {
|
from_currency: from_currency,
|
||||||
from_currency: from_currency,
|
to_currency: company_currency,
|
||||||
to_currency: company_currency,
|
},
|
||||||
},
|
callback: function(r) {
|
||||||
callback: function(r) {
|
frm.set_value("exchange_rate", flt(r.message));
|
||||||
frm.set_value("exchange_rate", flt(r.message));
|
frm.set_df_property("exchange_rate", "hidden", 0);
|
||||||
frm.set_df_property('exchange_rate', 'hidden', 0);
|
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
|
||||||
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency
|
+ " = [?] " + company_currency);
|
||||||
+ " = [?] " + company_currency);
|
}
|
||||||
}
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
frm.set_value("exchange_rate", 1.0);
|
||||||
frm.set_value("exchange_rate", 1.0);
|
frm.set_df_property("exchange_rate", "hidden", 1);
|
||||||
frm.set_df_property('exchange_rate', 'hidden', 1);
|
frm.set_df_property("exchange_rate", "description", "");
|
||||||
frm.set_df_property("exchange_rate", "description", "" );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -13,7 +13,7 @@ from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_da
|
|||||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
from frappe.utils.background_jobs import enqueue
|
from frappe.utils.background_jobs import enqueue
|
||||||
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salary_component
|
from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries
|
||||||
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period
|
||||||
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount
|
||||||
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
|
from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits
|
||||||
@ -524,7 +524,7 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
except NameError as err:
|
except NameError as err:
|
||||||
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
||||||
title=_("Name error"))
|
title=_("Name error"))
|
||||||
except SyntaxError as err:
|
except SyntaxError as err:
|
||||||
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
frappe.throw(_("Syntax error in formula or condition: {0}").format(err))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -558,15 +558,16 @@ class SalarySlip(TransactionBase):
|
|||||||
self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
|
self.update_component_row(frappe._dict(last_benefit.struct_row), amount, "earnings")
|
||||||
|
|
||||||
def add_additional_salary_components(self, component_type):
|
def add_additional_salary_components(self, component_type):
|
||||||
salary_components_details, additional_salary_details = get_additional_salary_component(self.employee,
|
additional_salaries = get_additional_salaries(self.employee,
|
||||||
self.start_date, self.end_date, component_type)
|
self.start_date, self.end_date, component_type)
|
||||||
if salary_components_details and additional_salary_details:
|
|
||||||
for additional_salary in additional_salary_details:
|
for additional_salary in additional_salaries:
|
||||||
additional_salary =frappe._dict(additional_salary)
|
self.update_component_row(
|
||||||
amount = additional_salary.amount
|
get_salary_component_data(additional_salary.component),
|
||||||
overwrite = additional_salary.overwrite
|
additional_salary.amount,
|
||||||
self.update_component_row(frappe._dict(salary_components_details[additional_salary.component]), amount,
|
component_type,
|
||||||
component_type, overwrite=overwrite, additional_salary=additional_salary.name)
|
additional_salary
|
||||||
|
)
|
||||||
|
|
||||||
def add_tax_components(self, payroll_period):
|
def add_tax_components(self, payroll_period):
|
||||||
# Calculate variable_based_on_taxable_salary after all components updated in salary slip
|
# Calculate variable_based_on_taxable_salary after all components updated in salary slip
|
||||||
@ -583,47 +584,59 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
for d in tax_components:
|
for d in tax_components:
|
||||||
tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
|
tax_amount = self.calculate_variable_based_on_taxable_salary(d, payroll_period)
|
||||||
tax_row = self.get_salary_slip_row(d)
|
tax_row = get_salary_component_data(d)
|
||||||
self.update_component_row(tax_row, tax_amount, "deductions")
|
self.update_component_row(tax_row, tax_amount, "deductions")
|
||||||
|
|
||||||
def update_component_row(self, struct_row, amount, key, overwrite=1, additional_salary = ''):
|
def update_component_row(self, component_data, amount, component_type, additional_salary=None):
|
||||||
component_row = None
|
component_row = None
|
||||||
for d in self.get(key):
|
for d in self.get(component_type):
|
||||||
if d.salary_component == struct_row.salary_component:
|
if d.salary_component != component_data.salary_component:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (
|
||||||
|
not d.additional_salary
|
||||||
|
and (not additional_salary or additional_salary.overwrite)
|
||||||
|
or additional_salary
|
||||||
|
and additional_salary.name == d.additional_salary
|
||||||
|
):
|
||||||
component_row = d
|
component_row = d
|
||||||
|
break
|
||||||
|
|
||||||
if not component_row or (struct_row.get("is_additional_component") and not overwrite):
|
if additional_salary and additional_salary.overwrite:
|
||||||
if amount:
|
# Additional Salary with overwrite checked, remove default rows of same component
|
||||||
self.append(key, {
|
self.set(component_type, [
|
||||||
'amount': amount,
|
d for d in self.get(component_type)
|
||||||
'default_amount': amount if not struct_row.get("is_additional_component") else 0,
|
if d.salary_component != component_data.salary_component
|
||||||
'depends_on_payment_days' : struct_row.depends_on_payment_days,
|
or d.additional_salary and additional_salary.name != d.additional_salary
|
||||||
'salary_component' : struct_row.salary_component,
|
or d == component_row
|
||||||
'abbr' : struct_row.abbr or struct_row.get("salary_component_abbr"),
|
])
|
||||||
'additional_salary': additional_salary,
|
|
||||||
'do_not_include_in_total' : struct_row.do_not_include_in_total,
|
if not component_row:
|
||||||
'is_tax_applicable': struct_row.is_tax_applicable,
|
if not amount:
|
||||||
'is_flexible_benefit': struct_row.is_flexible_benefit,
|
return
|
||||||
'variable_based_on_taxable_salary': struct_row.variable_based_on_taxable_salary,
|
|
||||||
'deduct_full_tax_on_selected_payroll_date': struct_row.deduct_full_tax_on_selected_payroll_date,
|
component_row = self.append(component_type)
|
||||||
'additional_amount': amount if struct_row.get("is_additional_component") else 0,
|
for attr in (
|
||||||
'exempted_from_income_tax': struct_row.exempted_from_income_tax
|
'depends_on_payment_days', 'salary_component', 'abbr'
|
||||||
})
|
'do_not_include_in_total', 'is_tax_applicable',
|
||||||
|
'is_flexible_benefit', 'variable_based_on_taxable_salary',
|
||||||
|
'exempted_from_income_tax'
|
||||||
|
):
|
||||||
|
component_row.set(attr, component_data.get(attr))
|
||||||
|
|
||||||
|
if additional_salary:
|
||||||
|
component_row.default_amount = 0
|
||||||
|
component_row.additional_amount = amount
|
||||||
|
component_row.additional_salary = additional_salary.name
|
||||||
|
component_row.deduct_full_tax_on_selected_payroll_date = \
|
||||||
|
additional_salary.deduct_full_tax_on_selected_payroll_date
|
||||||
else:
|
else:
|
||||||
if struct_row.get("is_additional_component"):
|
component_row.default_amount = amount
|
||||||
if overwrite:
|
component_row.additional_amount = 0
|
||||||
component_row.additional_amount = amount - component_row.get("default_amount", 0)
|
component_row.deduct_full_tax_on_selected_payroll_date = \
|
||||||
component_row.additional_salary = additional_salary
|
component_data.deduct_full_tax_on_selected_payroll_date
|
||||||
else:
|
|
||||||
component_row.additional_amount = amount
|
|
||||||
|
|
||||||
if not overwrite and component_row.default_amount:
|
component_row.amount = amount
|
||||||
amount += component_row.default_amount
|
|
||||||
else:
|
|
||||||
component_row.default_amount = amount
|
|
||||||
|
|
||||||
component_row.amount = amount
|
|
||||||
component_row.deduct_full_tax_on_selected_payroll_date = struct_row.deduct_full_tax_on_selected_payroll_date
|
|
||||||
|
|
||||||
def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
|
def calculate_variable_based_on_taxable_salary(self, tax_component, payroll_period):
|
||||||
if not payroll_period:
|
if not payroll_period:
|
||||||
@ -950,26 +963,13 @@ class SalarySlip(TransactionBase):
|
|||||||
return frappe.safe_eval(condition, self.whitelisted_globals, data)
|
return frappe.safe_eval(condition, self.whitelisted_globals, data)
|
||||||
except NameError as err:
|
except NameError as err:
|
||||||
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
frappe.throw(_("{0} <br> This error can be due to missing or deleted field.").format(err),
|
||||||
title=_("Name error"))
|
title=_("Name error"))
|
||||||
except SyntaxError as err:
|
except SyntaxError as err:
|
||||||
frappe.throw(_("Syntax error in condition: {0}").format(err))
|
frappe.throw(_("Syntax error in condition: {0}").format(err))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
frappe.throw(_("Error in formula or condition: {0}").format(e))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_salary_slip_row(self, salary_component):
|
|
||||||
component = frappe.get_doc("Salary Component", salary_component)
|
|
||||||
# Data for update_component_row
|
|
||||||
struct_row = frappe._dict()
|
|
||||||
struct_row['depends_on_payment_days'] = component.depends_on_payment_days
|
|
||||||
struct_row['salary_component'] = component.name
|
|
||||||
struct_row['abbr'] = component.salary_component_abbr
|
|
||||||
struct_row['do_not_include_in_total'] = component.do_not_include_in_total
|
|
||||||
struct_row['is_tax_applicable'] = component.is_tax_applicable
|
|
||||||
struct_row['is_flexible_benefit'] = component.is_flexible_benefit
|
|
||||||
struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
|
|
||||||
return struct_row
|
|
||||||
|
|
||||||
def get_component_totals(self, component_type, depends_on_payment_days=0):
|
def get_component_totals(self, component_type, depends_on_payment_days=0):
|
||||||
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
|
||||||
["date_of_joining", "relieving_date"])
|
["date_of_joining", "relieving_date"])
|
||||||
@ -1032,7 +1032,6 @@ class SalarySlip(TransactionBase):
|
|||||||
self.total_loan_repayment += payment.total_payment
|
self.total_loan_repayment += payment.total_payment
|
||||||
|
|
||||||
def get_loan_details(self):
|
def get_loan_details(self):
|
||||||
|
|
||||||
return frappe.get_all("Loan",
|
return frappe.get_all("Loan",
|
||||||
fields=["name", "interest_income_account", "loan_account", "loan_type"],
|
fields=["name", "interest_income_account", "loan_account", "loan_type"],
|
||||||
filters = {
|
filters = {
|
||||||
@ -1263,3 +1262,19 @@ def unlink_ref_doc_from_salary_slip(ref_no):
|
|||||||
def generate_password_for_pdf(policy_template, employee):
|
def generate_password_for_pdf(policy_template, employee):
|
||||||
employee = frappe.get_doc("Employee", employee)
|
employee = frappe.get_doc("Employee", employee)
|
||||||
return policy_template.format(**employee.as_dict())
|
return policy_template.format(**employee.as_dict())
|
||||||
|
|
||||||
|
def get_salary_component_data(component):
|
||||||
|
return frappe.get_value(
|
||||||
|
"Salary Component",
|
||||||
|
component,
|
||||||
|
[
|
||||||
|
"name as salary_component",
|
||||||
|
"depends_on_payment_days",
|
||||||
|
"salary_component_abbr as abbr",
|
||||||
|
"do_not_include_in_total",
|
||||||
|
"is_tax_applicable",
|
||||||
|
"is_flexible_benefit",
|
||||||
|
"variable_based_on_taxable_salary",
|
||||||
|
],
|
||||||
|
as_dict=1,
|
||||||
|
)
|
||||||
|
@ -361,7 +361,6 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
|
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
|
||||||
frappe.db.sql("""delete from `tabPayroll Period`""")
|
frappe.db.sql("""delete from `tabPayroll Period`""")
|
||||||
frappe.db.sql("""delete from `tabSalary Component`""")
|
frappe.db.sql("""delete from `tabSalary Component`""")
|
||||||
frappe.db.sql("""delete from `tabAdditional Salary`""")
|
|
||||||
|
|
||||||
payroll_period = create_payroll_period()
|
payroll_period = create_payroll_period()
|
||||||
|
|
||||||
|
@ -10,8 +10,38 @@ from erpnext.stock.doctype.item.test_item import make_item_variant
|
|||||||
test_dependencies = ["Item"]
|
test_dependencies = ["Item"]
|
||||||
|
|
||||||
class TestProductConfigurator(unittest.TestCase):
|
class TestProductConfigurator(unittest.TestCase):
|
||||||
def setUp(self):
|
@classmethod
|
||||||
self.create_variant_item()
|
def setUpClass(cls):
|
||||||
|
cls.create_variant_item()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def create_variant_item(cls):
|
||||||
|
if not frappe.db.exists('Item', '_Test Variant Item - 2XL'):
|
||||||
|
frappe.get_doc({
|
||||||
|
"description": "_Test Variant Item - 2XL",
|
||||||
|
"item_code": "_Test Variant Item - 2XL",
|
||||||
|
"item_name": "_Test Variant Item - 2XL",
|
||||||
|
"doctype": "Item",
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"variant_of": "_Test Variant Item",
|
||||||
|
"item_group": "_Test Item Group",
|
||||||
|
"stock_uom": "_Test UOM",
|
||||||
|
"item_defaults": [{
|
||||||
|
"company": "_Test Company",
|
||||||
|
"default_warehouse": "_Test Warehouse - _TC",
|
||||||
|
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
||||||
|
"buying_cost_center": "_Test Cost Center - _TC",
|
||||||
|
"selling_cost_center": "_Test Cost Center - _TC",
|
||||||
|
"income_account": "Sales - _TC"
|
||||||
|
}],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"attribute": "Test Size",
|
||||||
|
"attribute_value": "2XL"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"show_variant_in_website": 1
|
||||||
|
}).insert()
|
||||||
|
|
||||||
def test_product_list(self):
|
def test_product_list(self):
|
||||||
template_items = frappe.get_all('Item', {'show_in_website': 1})
|
template_items = frappe.get_all('Item', {'show_in_website': 1})
|
||||||
@ -46,39 +76,6 @@ class TestProductConfigurator(unittest.TestCase):
|
|||||||
|
|
||||||
def test_get_products_for_website(self):
|
def test_get_products_for_website(self):
|
||||||
items = get_products_for_website(attribute_filters={
|
items = get_products_for_website(attribute_filters={
|
||||||
'Test Size': ['Medium']
|
'Test Size': ['2XL']
|
||||||
})
|
})
|
||||||
self.assertEqual(len(items), 1)
|
self.assertEqual(len(items), 1)
|
||||||
|
|
||||||
|
|
||||||
def create_variant_item(self):
|
|
||||||
if not frappe.db.exists('Item', '_Test Variant Item 1'):
|
|
||||||
frappe.get_doc({
|
|
||||||
"description": "_Test Variant Item 12",
|
|
||||||
"doctype": "Item",
|
|
||||||
"is_stock_item": 1,
|
|
||||||
"variant_of": "_Test Variant Item",
|
|
||||||
"item_code": "_Test Variant Item 1",
|
|
||||||
"item_group": "_Test Item Group",
|
|
||||||
"item_name": "_Test Variant Item 1",
|
|
||||||
"stock_uom": "_Test UOM",
|
|
||||||
"item_defaults": [{
|
|
||||||
"company": "_Test Company",
|
|
||||||
"default_warehouse": "_Test Warehouse - _TC",
|
|
||||||
"expense_account": "_Test Account Cost for Goods Sold - _TC",
|
|
||||||
"buying_cost_center": "_Test Cost Center - _TC",
|
|
||||||
"selling_cost_center": "_Test Cost Center - _TC",
|
|
||||||
"income_account": "Sales - _TC"
|
|
||||||
}],
|
|
||||||
"attributes": [
|
|
||||||
{
|
|
||||||
"attribute": "Test Size",
|
|
||||||
"attribute_value": "Medium"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"show_variant_in_website": 1
|
|
||||||
}).insert()
|
|
||||||
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
frappe.db.rollback()
|
|
@ -298,7 +298,7 @@ def get_items_by_fields(field_filters):
|
|||||||
|
|
||||||
|
|
||||||
def get_items(filters=None, search=None):
|
def get_items(filters=None, search=None):
|
||||||
start = frappe.form_dict.start or 0
|
start = frappe.form_dict.get('start', 0)
|
||||||
products_settings = get_product_settings()
|
products_settings = get_product_settings()
|
||||||
page_length = products_settings.products_per_page
|
page_length = products_settings.products_per_page
|
||||||
|
|
||||||
|
@ -81,12 +81,18 @@ class Project(Document):
|
|||||||
|
|
||||||
def calculate_start_date(self, task_details):
|
def calculate_start_date(self, task_details):
|
||||||
self.start_date = add_days(self.expected_start_date, task_details.start)
|
self.start_date = add_days(self.expected_start_date, task_details.start)
|
||||||
self.start_date = update_if_holiday(self.holiday_list, self.start_date)
|
self.start_date = self.update_if_holiday(self.start_date)
|
||||||
return self.start_date
|
return self.start_date
|
||||||
|
|
||||||
def calculate_end_date(self, task_details):
|
def calculate_end_date(self, task_details):
|
||||||
self.end_date = add_days(self.start_date, task_details.duration)
|
self.end_date = add_days(self.start_date, task_details.duration)
|
||||||
return update_if_holiday(self.holiday_list, self.end_date)
|
return self.update_if_holiday(self.end_date)
|
||||||
|
|
||||||
|
def update_if_holiday(self, date):
|
||||||
|
holiday_list = self.holiday_list or get_holiday_list(self.company)
|
||||||
|
while is_holiday(holiday_list, date):
|
||||||
|
date = add_days(date, 1)
|
||||||
|
return date
|
||||||
|
|
||||||
def dependency_mapping(self, template_tasks, project_tasks):
|
def dependency_mapping(self, template_tasks, project_tasks):
|
||||||
for template_task in template_tasks:
|
for template_task in template_tasks:
|
||||||
@ -541,9 +547,3 @@ def set_project_status(project, status):
|
|||||||
|
|
||||||
project.status = status
|
project.status = status
|
||||||
project.save()
|
project.save()
|
||||||
|
|
||||||
def update_if_holiday(holiday_list, date):
|
|
||||||
holiday_list = holiday_list or get_holiday_list()
|
|
||||||
while is_holiday(holiday_list, date):
|
|
||||||
date = add_days(date, 1)
|
|
||||||
return date
|
|
||||||
|
@ -8,7 +8,6 @@ test_records = frappe.get_test_records('Project')
|
|||||||
test_ignore = ["Sales Order"]
|
test_ignore = ["Sales Order"]
|
||||||
|
|
||||||
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
|
from erpnext.projects.doctype.project_template.test_project_template import make_project_template
|
||||||
from erpnext.projects.doctype.project.project import update_if_holiday
|
|
||||||
from erpnext.projects.doctype.task.test_task import create_task
|
from erpnext.projects.doctype.task.test_task import create_task
|
||||||
from frappe.utils import getdate, nowdate, add_days
|
from frappe.utils import getdate, nowdate, add_days
|
||||||
|
|
||||||
@ -97,7 +96,8 @@ def get_project(name, template):
|
|||||||
project_name = name,
|
project_name = name,
|
||||||
status = 'Open',
|
status = 'Open',
|
||||||
project_template = template.name,
|
project_template = template.name,
|
||||||
expected_start_date = nowdate()
|
expected_start_date = nowdate(),
|
||||||
|
company="_Test Company"
|
||||||
)).insert()
|
)).insert()
|
||||||
|
|
||||||
return project
|
return project
|
||||||
@ -131,7 +131,7 @@ def task_exists(subject):
|
|||||||
|
|
||||||
def calculate_end_date(project, start, duration):
|
def calculate_end_date(project, start, duration):
|
||||||
start = add_days(project.expected_start_date, start)
|
start = add_days(project.expected_start_date, start)
|
||||||
start = update_if_holiday(project.holiday_list, start)
|
start = project.update_if_holiday(start)
|
||||||
end = add_days(start, duration)
|
end = add_days(start, duration)
|
||||||
end = update_if_holiday(project.holiday_list, end)
|
end = project.update_if_holiday(end)
|
||||||
return getdate(end)
|
return getdate(end)
|
||||||
|
@ -13,9 +13,18 @@ from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.payroll.doctype.salary_structure.test_salary_structure \
|
from erpnext.payroll.doctype.salary_structure.test_salary_structure \
|
||||||
import make_salary_structure, create_salary_structure_assignment
|
import make_salary_structure, create_salary_structure_assignment
|
||||||
|
from erpnext.payroll.doctype.salary_slip.test_salary_slip import (
|
||||||
|
make_earning_salary_component,
|
||||||
|
make_deduction_salary_component
|
||||||
|
)
|
||||||
from erpnext.hr.doctype.employee.test_employee import make_employee
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
class TestTimesheet(unittest.TestCase):
|
class TestTimesheet(unittest.TestCase):
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
make_earning_salary_component(setup=True, company_list=['_Test Company'])
|
||||||
|
make_deduction_salary_component(setup=True, company_list=['_Test Company'])
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
|
||||||
frappe.db.sql("delete from `tab%s`" % dt)
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
@ -49,7 +58,7 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(timesheet.total_billable_amount, 0)
|
self.assertEqual(timesheet.total_billable_amount, 0)
|
||||||
|
|
||||||
def test_salary_slip_from_timesheet(self):
|
def test_salary_slip_from_timesheet(self):
|
||||||
emp = make_employee("test_employee_6@salary.com")
|
emp = make_employee("test_employee_6@salary.com", company="_Test Company")
|
||||||
|
|
||||||
salary_structure = make_salary_structure_for_timesheet(emp)
|
salary_structure = make_salary_structure_for_timesheet(emp)
|
||||||
timesheet = make_timesheet(emp, simulate = True, billable=1)
|
timesheet = make_timesheet(emp, simulate = True, billable=1)
|
||||||
|
@ -577,7 +577,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
var d = locals[cdt][cdn];
|
var d = locals[cdt][cdn];
|
||||||
me.add_taxes_from_item_tax_template(d.item_tax_rate);
|
me.add_taxes_from_item_tax_template(d.item_tax_rate);
|
||||||
if (d.free_item_data) {
|
if (d.free_item_data) {
|
||||||
me.apply_product_discount(d.free_item_data);
|
me.apply_product_discount(d);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
@ -738,21 +738,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var valid_serial_nos = [];
|
var valid_serial_nos = [];
|
||||||
|
var serialnos = [];
|
||||||
// Replacing all occurences of comma with carriage return
|
// Replacing all occurences of comma with carriage return
|
||||||
var serial_nos = item.serial_no.trim().replace(/,/g, '\n');
|
item.serial_no = item.serial_no.replace(/,/g, '\n');
|
||||||
|
serialnos = item.serial_no.split("\n");
|
||||||
serial_nos = serial_nos.trim().split('\n');
|
for (var i = 0; i < serialnos.length; i++) {
|
||||||
|
if (serialnos[i] != "") {
|
||||||
// Trim each string and push unique string to new list
|
valid_serial_nos.push(serialnos[i]);
|
||||||
for (var x=0; x<=serial_nos.length - 1; x++) {
|
|
||||||
if (serial_nos[x].trim() != "" && valid_serial_nos.indexOf(serial_nos[x].trim()) == -1) {
|
|
||||||
valid_serial_nos.push(serial_nos[x].trim());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the new list to the serial no. field in grid with each in new line
|
|
||||||
item.serial_no = valid_serial_nos.join('\n');
|
|
||||||
item.conversion_factor = item.conversion_factor || 1;
|
item.conversion_factor = item.conversion_factor || 1;
|
||||||
|
|
||||||
refresh_field("serial_no", item.name, item.parentfield);
|
refresh_field("serial_no", item.name, item.parentfield);
|
||||||
@ -1204,7 +1198,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
|
|
||||||
calculate_stock_uom_rate: function(doc, cdt, cdn) {
|
calculate_stock_uom_rate: function(doc, cdt, cdn) {
|
||||||
let item = frappe.get_doc(cdt, cdn);
|
let item = frappe.get_doc(cdt, cdn);
|
||||||
item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);
|
item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);
|
||||||
refresh_field("stock_uom_rate", item.name, item.parentfield);
|
refresh_field("stock_uom_rate", item.name, item.parentfield);
|
||||||
},
|
},
|
||||||
service_stop_date: function(frm, cdt, cdn) {
|
service_stop_date: function(frm, cdt, cdn) {
|
||||||
@ -1533,7 +1527,10 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
if(k=="price_list_rate") {
|
if(k=="price_list_rate") {
|
||||||
if(flt(v) != flt(d.price_list_rate)) price_list_rate_changed = true;
|
if(flt(v) != flt(d.price_list_rate)) price_list_rate_changed = true;
|
||||||
}
|
}
|
||||||
frappe.model.set_value(d.doctype, d.name, k, v);
|
|
||||||
|
if (k !== 'free_item_data') {
|
||||||
|
frappe.model.set_value(d.doctype, d.name, k, v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1545,7 +1542,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (d.free_item_data) {
|
if (d.free_item_data) {
|
||||||
me.apply_product_discount(d.free_item_data);
|
me.apply_product_discount(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d.apply_rule_on_other_items) {
|
if (d.apply_rule_on_other_items) {
|
||||||
@ -1579,20 +1576,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
apply_product_discount: function(free_item_data) {
|
apply_product_discount: function(args) {
|
||||||
const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code
|
const items = this.frm.doc.items.filter(d => (d.is_free_item)) || [];
|
||||||
&& d.is_free_item)) || [];
|
|
||||||
|
|
||||||
if (!items.length) {
|
const exist_items = items.map(row => (row.item_code, row.pricing_rules));
|
||||||
let row_to_modify = frappe.model.add_child(this.frm.doc,
|
|
||||||
this.frm.doc.doctype + ' Item', 'items');
|
|
||||||
|
|
||||||
for (let key in free_item_data) {
|
args.free_item_data.forEach(pr_row => {
|
||||||
row_to_modify[key] = free_item_data[key];
|
let row_to_modify = {};
|
||||||
|
if (!items || !in_list(exist_items, (pr_row.item_code, pr_row.pricing_rules))) {
|
||||||
|
|
||||||
|
row_to_modify = frappe.model.add_child(this.frm.doc,
|
||||||
|
this.frm.doc.doctype + ' Item', 'items');
|
||||||
|
|
||||||
|
} else if(items) {
|
||||||
|
row_to_modify = items.filter(d => (d.item_code === pr_row.item_code
|
||||||
|
&& d.pricing_rules === pr_row.pricing_rules))[0];
|
||||||
}
|
}
|
||||||
} if (items && items.length && free_item_data) {
|
|
||||||
items[0].qty = free_item_data.qty
|
for (let key in pr_row) {
|
||||||
}
|
row_to_modify[key] = pr_row[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// free_item_data is a temporary variable
|
||||||
|
args.free_item_data = '';
|
||||||
|
refresh_field('items');
|
||||||
},
|
},
|
||||||
|
|
||||||
apply_price_list: function(item, reset_plc_conversion) {
|
apply_price_list: function(item, reset_plc_conversion) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -349,13 +349,12 @@ class GSTR3BReport(Document):
|
|||||||
return inter_state_supply_details
|
return inter_state_supply_details
|
||||||
|
|
||||||
def get_inward_nil_exempt(self, state):
|
def get_inward_nil_exempt(self, state):
|
||||||
|
|
||||||
inward_nil_exempt = frappe.db.sql(""" select p.place_of_supply, sum(i.base_amount) as base_amount,
|
inward_nil_exempt = frappe.db.sql(""" select p.place_of_supply, sum(i.base_amount) as base_amount,
|
||||||
i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
|
i.is_nil_exempt, i.is_non_gst from `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
|
||||||
where p.docstatus = 1 and p.name = i.parent
|
where p.docstatus = 1 and p.name = i.parent
|
||||||
and i.is_nil_exempt = 1 or i.is_non_gst = 1 and
|
and (i.is_nil_exempt = 1 or i.is_non_gst = 1) and
|
||||||
month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
|
month(p.posting_date) = %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s
|
||||||
group by p.place_of_supply """, (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
|
group by p.place_of_supply, i.is_nil_exempt, i.is_non_gst""", (self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
|
||||||
|
|
||||||
inward_nil_exempt_details = {
|
inward_nil_exempt_details = {
|
||||||
"gst": {
|
"gst": {
|
||||||
|
@ -16,6 +16,7 @@ class TaxExemption80GCertificate(Document):
|
|||||||
self.validate_duplicates()
|
self.validate_duplicates()
|
||||||
self.validate_company_details()
|
self.validate_company_details()
|
||||||
self.set_company_address()
|
self.set_company_address()
|
||||||
|
self.calculate_total()
|
||||||
self.set_title()
|
self.set_title()
|
||||||
|
|
||||||
def validate_date(self):
|
def validate_date(self):
|
||||||
@ -29,7 +30,10 @@ class TaxExemption80GCertificate(Document):
|
|||||||
|
|
||||||
def validate_duplicates(self):
|
def validate_duplicates(self):
|
||||||
if self.recipient == 'Donor':
|
if self.recipient == 'Donor':
|
||||||
certificate = frappe.db.exists(self.doctype, {'donation': self.donation})
|
certificate = frappe.db.exists(self.doctype, {
|
||||||
|
'donation': self.donation,
|
||||||
|
'name': ('!=', self.name)
|
||||||
|
})
|
||||||
if certificate:
|
if certificate:
|
||||||
frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
|
frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format(
|
||||||
get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
|
get_link_to_form(self.doctype, certificate), frappe.bold(self.donation)
|
||||||
@ -51,8 +55,17 @@ class TaxExemption80GCertificate(Document):
|
|||||||
self.company_address = address.company_address
|
self.company_address = address.company_address
|
||||||
self.company_address_display = address.company_address_display
|
self.company_address_display = address.company_address_display
|
||||||
|
|
||||||
|
def calculate_total(self):
|
||||||
|
if self.recipient == 'Donor':
|
||||||
|
return
|
||||||
|
|
||||||
|
total = 0
|
||||||
|
for entry in self.payments:
|
||||||
|
total += flt(entry.amount)
|
||||||
|
self.total = total
|
||||||
|
|
||||||
def set_title(self):
|
def set_title(self):
|
||||||
if self.recipient == "Member":
|
if self.recipient == 'Member':
|
||||||
self.title = self.member_name
|
self.title = self.member_name
|
||||||
else:
|
else:
|
||||||
self.title = self.donor_name
|
self.title = self.donor_name
|
||||||
@ -68,7 +81,7 @@ class TaxExemption80GCertificate(Document):
|
|||||||
'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
|
'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
|
||||||
'to_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
|
'to_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)],
|
||||||
'membership_status': ('!=', 'Cancelled')
|
'membership_status': ('!=', 'Cancelled')
|
||||||
}, ['from_date', 'amount', 'name', 'invoice', 'payment_id'])
|
}, ['from_date', 'amount', 'name', 'invoice', 'payment_id'], order_by='from_date')
|
||||||
|
|
||||||
if not memberships:
|
if not memberships:
|
||||||
frappe.msgprint(_('No Membership Payments found against the Member {0}').format(self.member))
|
frappe.msgprint(_('No Membership Payments found against the Member {0}').format(self.member))
|
||||||
|
@ -808,3 +808,27 @@ def get_gst_tax_amount(doc):
|
|||||||
gst_tax += tax.tax_amount_after_discount_amount
|
gst_tax += tax.tax_amount_after_discount_amount
|
||||||
|
|
||||||
return gst_tax, base_gst_tax
|
return gst_tax, base_gst_tax
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_regional_round_off_accounts(company, account_list):
|
||||||
|
country = frappe.get_cached_value('Company', company, 'country')
|
||||||
|
|
||||||
|
if country != 'India':
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(account_list, string_types):
|
||||||
|
account_list = json.loads(account_list)
|
||||||
|
|
||||||
|
if not frappe.db.get_single_value('GST Settings', 'round_off_gst_values'):
|
||||||
|
return
|
||||||
|
|
||||||
|
gst_accounts = get_gst_accounts(company)
|
||||||
|
|
||||||
|
gst_account_list = []
|
||||||
|
for account in ['cgst_account', 'sgst_account', 'igst_account']:
|
||||||
|
if account in gst_accounts:
|
||||||
|
gst_account_list += gst_accounts.get(account)
|
||||||
|
|
||||||
|
account_list.extend(gst_account_list)
|
||||||
|
|
||||||
|
return account_list
|
||||||
|
@ -11,15 +11,10 @@ erpnext.setup_e_invoice_button = (doctype) => {
|
|||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
frm.reload_doc();
|
frm.reload_doc();
|
||||||
if(r.message) {
|
if(r.message) {
|
||||||
var w = window.open(
|
open_url_post(frappe.request.url, {
|
||||||
frappe.urllib.get_full_url(
|
cmd: 'frappe.core.doctype.file.file.download_file',
|
||||||
"/api/method/erpnext.regional.italy.utils.download_e_invoice_file?"
|
file_url: r.message
|
||||||
+ "file_name=" + r.message
|
});
|
||||||
)
|
|
||||||
)
|
|
||||||
if (!w) {
|
|
||||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -128,11 +128,8 @@ def make_custom_fields(update=True):
|
|||||||
fetch_from="company.vat_collectability"),
|
fetch_from="company.vat_collectability"),
|
||||||
dict(fieldname='sb_e_invoicing_reference', label='E-Invoicing',
|
dict(fieldname='sb_e_invoicing_reference', label='E-Invoicing',
|
||||||
fieldtype='Section Break', insert_after='against_income_account', print_hide=1),
|
fieldtype='Section Break', insert_after='against_income_account', print_hide=1),
|
||||||
dict(fieldname='company_tax_id', label='Company Tax ID',
|
|
||||||
fieldtype='Data', insert_after='sb_e_invoicing_reference', print_hide=1, read_only=1,
|
|
||||||
fetch_from="company.tax_id"),
|
|
||||||
dict(fieldname='company_fiscal_code', label='Company Fiscal Code',
|
dict(fieldname='company_fiscal_code', label='Company Fiscal Code',
|
||||||
fieldtype='Data', insert_after='company_tax_id', print_hide=1, read_only=1,
|
fieldtype='Data', insert_after='sb_e_invoicing_reference', print_hide=1, read_only=1,
|
||||||
fetch_from="company.fiscal_code"),
|
fetch_from="company.fiscal_code"),
|
||||||
dict(fieldname='company_fiscal_regime', label='Company Fiscal Regime',
|
dict(fieldname='company_fiscal_regime', label='Company Fiscal Regime',
|
||||||
fieldtype='Data', insert_after='company_fiscal_code', print_hide=1, read_only=1,
|
fieldtype='Data', insert_after='company_fiscal_code', print_hide=1, read_only=1,
|
||||||
@ -217,4 +214,4 @@ def add_permissions():
|
|||||||
update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1)
|
update_permission_property(doctype, 'Accounts Manager', 0, 'delete', 1)
|
||||||
add_permission(doctype, 'Accounts Manager', 1)
|
add_permission(doctype, 'Accounts Manager', 1)
|
||||||
update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1)
|
update_permission_property(doctype, 'Accounts Manager', 1, 'write', 1)
|
||||||
update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1)
|
update_permission_property(doctype, 'Accounts Manager', 1, 'create', 1)
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import frappe, json, os
|
import io
|
||||||
|
import json
|
||||||
|
import frappe
|
||||||
from frappe.utils import flt, cstr
|
from frappe.utils import flt, cstr
|
||||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax
|
from erpnext.controllers.taxes_and_totals import get_itemised_tax
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@ -28,20 +30,22 @@ def update_itemised_tax_data(doc):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def export_invoices(filters=None):
|
def export_invoices(filters=None):
|
||||||
saved_xmls = []
|
frappe.has_permission('Sales Invoice', throw=True)
|
||||||
|
|
||||||
invoices = frappe.get_all("Sales Invoice", filters=get_conditions(filters), fields=["*"])
|
invoices = frappe.get_all(
|
||||||
|
"Sales Invoice",
|
||||||
|
filters=get_conditions(filters),
|
||||||
|
fields=["name", "company_tax_id"]
|
||||||
|
)
|
||||||
|
|
||||||
for invoice in invoices:
|
attachments = get_e_invoice_attachments(invoices)
|
||||||
attachments = get_e_invoice_attachments(invoice)
|
|
||||||
saved_xmls += [attachment.file_name for attachment in attachments]
|
|
||||||
|
|
||||||
zip_filename = "{0}-einvoices.zip".format(frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
|
zip_filename = "{0}-einvoices.zip".format(
|
||||||
|
frappe.utils.get_datetime().strftime("%Y%m%d_%H%M%S"))
|
||||||
|
|
||||||
download_zip(saved_xmls, zip_filename)
|
download_zip(attachments, zip_filename)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def prepare_invoice(invoice, progressive_number):
|
def prepare_invoice(invoice, progressive_number):
|
||||||
#set company information
|
#set company information
|
||||||
company = frappe.get_doc("Company", invoice.company)
|
company = frappe.get_doc("Company", invoice.company)
|
||||||
@ -98,7 +102,7 @@ def prepare_invoice(invoice, progressive_number):
|
|||||||
def get_conditions(filters):
|
def get_conditions(filters):
|
||||||
filters = json.loads(filters)
|
filters = json.loads(filters)
|
||||||
|
|
||||||
conditions = {"docstatus": 1}
|
conditions = {"docstatus": 1, "company_tax_id": ("!=", "")}
|
||||||
|
|
||||||
if filters.get("company"): conditions["company"] = filters["company"]
|
if filters.get("company"): conditions["company"] = filters["company"]
|
||||||
if filters.get("customer"): conditions["customer"] = filters["customer"]
|
if filters.get("customer"): conditions["customer"] = filters["customer"]
|
||||||
@ -111,23 +115,22 @@ def get_conditions(filters):
|
|||||||
|
|
||||||
return conditions
|
return conditions
|
||||||
|
|
||||||
#TODO: Use function from frappe once PR #6853 is merged.
|
|
||||||
def download_zip(files, output_filename):
|
def download_zip(files, output_filename):
|
||||||
from zipfile import ZipFile
|
import zipfile
|
||||||
|
|
||||||
input_files = [frappe.get_site_path('private', 'files', filename) for filename in files]
|
zip_stream = io.BytesIO()
|
||||||
output_path = frappe.get_site_path('private', 'files', output_filename)
|
with zipfile.ZipFile(zip_stream, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||||
|
for file in files:
|
||||||
|
file_path = frappe.utils.get_files_path(
|
||||||
|
file.file_name, is_private=file.is_private)
|
||||||
|
|
||||||
with ZipFile(output_path, 'w') as output_zip:
|
zip_file.write(file_path, arcname=file.file_name)
|
||||||
for input_file in input_files:
|
|
||||||
output_zip.write(input_file, arcname=os.path.basename(input_file))
|
|
||||||
|
|
||||||
with open(output_path, 'rb') as fileobj:
|
|
||||||
filedata = fileobj.read()
|
|
||||||
|
|
||||||
frappe.local.response.filename = output_filename
|
frappe.local.response.filename = output_filename
|
||||||
frappe.local.response.filecontent = filedata
|
frappe.local.response.filecontent = zip_stream.getvalue()
|
||||||
frappe.local.response.type = "download"
|
frappe.local.response.type = "download"
|
||||||
|
zip_stream.close()
|
||||||
|
|
||||||
def get_invoice_summary(items, taxes):
|
def get_invoice_summary(items, taxes):
|
||||||
summary_data = frappe._dict()
|
summary_data = frappe._dict()
|
||||||
@ -307,23 +310,12 @@ def prepare_and_attach_invoice(doc, replace=False):
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def generate_single_invoice(docname):
|
def generate_single_invoice(docname):
|
||||||
doc = frappe.get_doc("Sales Invoice", docname)
|
doc = frappe.get_doc("Sales Invoice", docname)
|
||||||
|
frappe.has_permission("Sales Invoice", doc=doc, throw=True)
|
||||||
|
|
||||||
e_invoice = prepare_and_attach_invoice(doc, True)
|
e_invoice = prepare_and_attach_invoice(doc, True)
|
||||||
|
return e_invoice.file_url
|
||||||
|
|
||||||
return e_invoice.file_name
|
# Delete e-invoice attachment on cancel.
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def download_e_invoice_file(file_name):
|
|
||||||
content = None
|
|
||||||
with open(frappe.get_site_path('private', 'files', file_name), "r") as f:
|
|
||||||
content = f.read()
|
|
||||||
|
|
||||||
frappe.local.response.filename = file_name
|
|
||||||
frappe.local.response.filecontent = content
|
|
||||||
frappe.local.response.type = "download"
|
|
||||||
|
|
||||||
#Delete e-invoice attachment on cancel.
|
|
||||||
def sales_invoice_on_cancel(doc, method):
|
def sales_invoice_on_cancel(doc, method):
|
||||||
if get_company_country(doc.company) not in ['Italy',
|
if get_company_country(doc.company) not in ['Italy',
|
||||||
'Italia', 'Italian Republic', 'Repubblica Italiana']:
|
'Italia', 'Italian Republic', 'Repubblica Italiana']:
|
||||||
@ -335,16 +327,38 @@ def sales_invoice_on_cancel(doc, method):
|
|||||||
def get_company_country(company):
|
def get_company_country(company):
|
||||||
return frappe.get_cached_value('Company', company, 'country')
|
return frappe.get_cached_value('Company', company, 'country')
|
||||||
|
|
||||||
def get_e_invoice_attachments(invoice):
|
def get_e_invoice_attachments(invoices):
|
||||||
if not invoice.company_tax_id:
|
if not isinstance(invoices, list):
|
||||||
return []
|
if not invoices.company_tax_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
invoices = [invoices]
|
||||||
|
|
||||||
|
tax_id_map = {
|
||||||
|
invoice.name: (
|
||||||
|
invoice.company_tax_id
|
||||||
|
if invoice.company_tax_id.startswith("IT")
|
||||||
|
else "IT" + invoice.company_tax_id
|
||||||
|
) for invoice in invoices
|
||||||
|
}
|
||||||
|
|
||||||
|
attachments = frappe.get_all(
|
||||||
|
"File",
|
||||||
|
fields=("name", "file_name", "attached_to_name", "is_private"),
|
||||||
|
filters= {
|
||||||
|
"attached_to_name": ('in', tax_id_map),
|
||||||
|
"attached_to_doctype": 'Sales Invoice'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
out = []
|
out = []
|
||||||
attachments = get_attachments(invoice.doctype, invoice.name)
|
|
||||||
company_tax_id = invoice.company_tax_id if invoice.company_tax_id.startswith("IT") else "IT" + invoice.company_tax_id
|
|
||||||
|
|
||||||
for attachment in attachments:
|
for attachment in attachments:
|
||||||
if attachment.file_name and attachment.file_name.startswith(company_tax_id) and attachment.file_name.endswith(".xml"):
|
if (
|
||||||
|
attachment.file_name
|
||||||
|
and attachment.file_name.endswith(".xml")
|
||||||
|
and attachment.file_name.startswith(
|
||||||
|
tax_id_map.get(attachment.attached_to_name))
|
||||||
|
):
|
||||||
out.append(attachment)
|
out.append(attachment)
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
@ -78,7 +78,7 @@ class Gstr1Report(object):
|
|||||||
place_of_supply = invoice_details.get("place_of_supply")
|
place_of_supply = invoice_details.get("place_of_supply")
|
||||||
ecommerce_gstin = invoice_details.get("ecommerce_gstin")
|
ecommerce_gstin = invoice_details.get("ecommerce_gstin")
|
||||||
|
|
||||||
b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin, inv),{
|
b2cs_output.setdefault((rate, place_of_supply, ecommerce_gstin),{
|
||||||
"place_of_supply": "",
|
"place_of_supply": "",
|
||||||
"ecommerce_gstin": "",
|
"ecommerce_gstin": "",
|
||||||
"rate": "",
|
"rate": "",
|
||||||
@ -90,7 +90,7 @@ class Gstr1Report(object):
|
|||||||
"invoice_value": invoice_details.get("base_grand_total"),
|
"invoice_value": invoice_details.get("base_grand_total"),
|
||||||
})
|
})
|
||||||
|
|
||||||
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin, inv))
|
row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin))
|
||||||
row["place_of_supply"] = place_of_supply
|
row["place_of_supply"] = place_of_supply
|
||||||
row["ecommerce_gstin"] = ecommerce_gstin
|
row["ecommerce_gstin"] = ecommerce_gstin
|
||||||
row["rate"] = rate
|
row["rate"] = rate
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
|
||||||
import json
|
import json
|
||||||
from frappe.utils import flt, add_days, nowdate
|
|
||||||
import frappe.permissions
|
|
||||||
import unittest
|
import unittest
|
||||||
|
import frappe
|
||||||
|
import frappe.permissions
|
||||||
|
from frappe.utils import flt, add_days, nowdate
|
||||||
|
from frappe.core.doctype.user_permission.test_user_permission import create_user
|
||||||
from erpnext.selling.doctype.sales_order.sales_order \
|
from erpnext.selling.doctype.sales_order.sales_order \
|
||||||
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired
|
||||||
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
|
||||||
@ -444,10 +445,8 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
def test_update_child_perm(self):
|
def test_update_child_perm(self):
|
||||||
so = make_sales_order(item_code= "_Test Item", qty=4)
|
so = make_sales_order(item_code= "_Test Item", qty=4)
|
||||||
|
|
||||||
user = 'test@example.com'
|
test_user = create_user("test_so_child_perms@example.com", "Accounts User")
|
||||||
test_user = frappe.get_doc('User', user)
|
frappe.set_user(test_user.name)
|
||||||
test_user.add_roles("Accounts User")
|
|
||||||
frappe.set_user(user)
|
|
||||||
|
|
||||||
# update qty
|
# update qty
|
||||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
|
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': so.items[0].name}])
|
||||||
@ -456,18 +455,14 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
# add new item
|
# add new item
|
||||||
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
|
trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}])
|
||||||
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name)
|
||||||
test_user.remove_roles("Accounts User")
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
|
|
||||||
def test_update_child_qty_rate_with_workflow(self):
|
def test_update_child_qty_rate_with_workflow(self):
|
||||||
from frappe.model.workflow import apply_workflow
|
from frappe.model.workflow import apply_workflow
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
workflow = make_sales_order_workflow()
|
workflow = make_sales_order_workflow()
|
||||||
so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
|
so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1)
|
||||||
apply_workflow(so, 'Approve')
|
apply_workflow(so, 'Approve')
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
user = 'test@example.com'
|
user = 'test@example.com'
|
||||||
test_user = frappe.get_doc('User', user)
|
test_user = frappe.get_doc('User', user)
|
||||||
test_user.add_roles("Sales User", "Test Junior Approver")
|
test_user.add_roles("Sales User", "Test Junior Approver")
|
||||||
@ -618,33 +613,31 @@ class TestSalesOrder(unittest.TestCase):
|
|||||||
frappe.db.set_value("Stock Settings", None, "default_warehouse", old_stock_settings_value)
|
frappe.db.set_value("Stock Settings", None, "default_warehouse", old_stock_settings_value)
|
||||||
|
|
||||||
def test_warehouse_user(self):
|
def test_warehouse_user(self):
|
||||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
|
test_user = create_user("test_so_warehouse_user@example.com", "Sales User", "Stock User")
|
||||||
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
|
||||||
frappe.permissions.add_user_permission("Company", "_Test Company 1", "test2@example.com")
|
|
||||||
|
|
||||||
test_user = frappe.get_doc("User", "test@example.com")
|
|
||||||
test_user.add_roles("Sales User", "Stock User")
|
|
||||||
test_user.remove_roles("Sales Manager")
|
|
||||||
|
|
||||||
test_user_2 = frappe.get_doc("User", "test2@example.com")
|
test_user_2 = frappe.get_doc("User", "test2@example.com")
|
||||||
test_user_2.add_roles("Sales User", "Stock User")
|
test_user_2.add_roles("Sales User", "Stock User")
|
||||||
test_user_2.remove_roles("Sales Manager")
|
test_user_2.remove_roles("Sales Manager")
|
||||||
|
|
||||||
frappe.set_user("test@example.com")
|
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
|
||||||
|
frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
|
||||||
|
frappe.permissions.add_user_permission("Company", "_Test Company 1", test_user_2.name)
|
||||||
|
|
||||||
so = make_sales_order(company="_Test Company 1",
|
frappe.set_user(test_user.name)
|
||||||
|
|
||||||
|
so = make_sales_order(company="_Test Company 1", customer="_Test Customer 1",
|
||||||
warehouse="_Test Warehouse 2 - _TC1", do_not_save=True)
|
warehouse="_Test Warehouse 2 - _TC1", do_not_save=True)
|
||||||
so.conversion_rate = 0.02
|
so.conversion_rate = 0.02
|
||||||
so.plc_conversion_rate = 0.02
|
so.plc_conversion_rate = 0.02
|
||||||
self.assertRaises(frappe.PermissionError, so.insert)
|
self.assertRaises(frappe.PermissionError, so.insert)
|
||||||
|
|
||||||
frappe.set_user("test2@example.com")
|
frappe.set_user(test_user_2.name)
|
||||||
so.insert()
|
so.insert()
|
||||||
|
|
||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com")
|
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 1 - _TC", test_user.name)
|
||||||
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com")
|
frappe.permissions.remove_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", test_user_2.name)
|
||||||
frappe.permissions.remove_user_permission("Company", "_Test Company 1", "test2@example.com")
|
frappe.permissions.remove_user_permission("Company", "_Test Company 1", test_user_2.name)
|
||||||
|
|
||||||
def test_block_delivery_note_against_cancelled_sales_order(self):
|
def test_block_delivery_note_against_cancelled_sales_order(self):
|
||||||
so = make_sales_order()
|
so = make_sales_order()
|
||||||
|
@ -397,6 +397,7 @@ erpnext.PointOfSale.Controller = class {
|
|||||||
this.recent_order_list.toggle_component(false);
|
this.recent_order_list.toggle_component(false);
|
||||||
frappe.run_serially([
|
frappe.run_serially([
|
||||||
() => this.frm.refresh(name),
|
() => this.frm.refresh(name),
|
||||||
|
() => this.frm.call('reset_mode_of_payments'),
|
||||||
() => this.cart.load_invoice(),
|
() => this.cart.load_invoice(),
|
||||||
() => this.item_selector.toggle_component(true)
|
() => this.item_selector.toggle_component(true)
|
||||||
]);
|
]);
|
||||||
|
@ -64,10 +64,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
|||||||
{fieldname: 'print', fieldtype: 'Data', label: 'Print Preview'}
|
{fieldname: 'print', fieldtype: 'Data', label: 'Print Preview'}
|
||||||
],
|
],
|
||||||
primary_action: () => {
|
primary_action: () => {
|
||||||
const frm = this.events.get_frm();
|
this.print_receipt();
|
||||||
frm.doc = this.doc;
|
|
||||||
frm.print_preview.lang_code = frm.doc.language;
|
|
||||||
frm.print_preview.printit(true);
|
|
||||||
},
|
},
|
||||||
primary_action_label: __('Print'),
|
primary_action_label: __('Print'),
|
||||||
});
|
});
|
||||||
@ -192,13 +189,21 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.$summary_container.on('click', '.print-btn', () => {
|
this.$summary_container.on('click', '.print-btn', () => {
|
||||||
const frm = this.events.get_frm();
|
this.print_receipt();
|
||||||
frm.doc = this.doc;
|
|
||||||
frm.print_preview.lang_code = frm.doc.language;
|
|
||||||
frm.print_preview.printit(true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print_receipt() {
|
||||||
|
const frm = this.events.get_frm();
|
||||||
|
frappe.utils.print(
|
||||||
|
frm.doctype,
|
||||||
|
frm.docname,
|
||||||
|
frm.pos_print_format,
|
||||||
|
frm.doc.letter_head,
|
||||||
|
frm.doc.language || frappe.boot.lang
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
attach_shortcuts() {
|
attach_shortcuts() {
|
||||||
const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
|
const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl';
|
||||||
this.$summary_container.find('.print-btn').attr("title", `${ctrl_label}+P`);
|
this.$summary_container.find('.print-btn').attr("title", `${ctrl_label}+P`);
|
||||||
|
@ -10,6 +10,7 @@ from frappe import msgprint, throw, _
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.model.naming import parse_naming_series
|
from frappe.model.naming import parse_naming_series
|
||||||
from frappe.permissions import get_doctypes_with_read
|
from frappe.permissions import get_doctypes_with_read
|
||||||
|
from frappe.core.doctype.doctype.doctype import validate_series
|
||||||
|
|
||||||
class NamingSeriesNotSetError(frappe.ValidationError): pass
|
class NamingSeriesNotSetError(frappe.ValidationError): pass
|
||||||
|
|
||||||
@ -126,7 +127,7 @@ class NamingSeries(Document):
|
|||||||
dt = frappe.get_doc("DocType", self.select_doc_for_series)
|
dt = frappe.get_doc("DocType", self.select_doc_for_series)
|
||||||
options = self.scrub_options_list(self.set_options.split("\n"))
|
options = self.scrub_options_list(self.set_options.split("\n"))
|
||||||
for series in options:
|
for series in options:
|
||||||
dt.validate_series(series)
|
validate_series(dt, series)
|
||||||
for i in sr:
|
for i in sr:
|
||||||
if i[0]:
|
if i[0]:
|
||||||
existing_series = [d.split('.')[0] for d in i[0].split("\n")]
|
existing_series = [d.split('.')[0] for d in i[0].split("\n")]
|
||||||
|
@ -142,13 +142,15 @@ def add_standard_navbar_items():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
current_nabvar_items = navbar_settings.help_dropdown
|
current_navbar_items = navbar_settings.help_dropdown
|
||||||
navbar_settings.set('help_dropdown', [])
|
navbar_settings.set('help_dropdown', [])
|
||||||
|
|
||||||
for item in erpnext_navbar_items:
|
for item in erpnext_navbar_items:
|
||||||
navbar_settings.append('help_dropdown', item)
|
current_labels = [item.get('item_label') for item in current_navbar_items]
|
||||||
|
if not item.get('item_label') in current_labels:
|
||||||
|
navbar_settings.append('help_dropdown', item)
|
||||||
|
|
||||||
for item in current_nabvar_items:
|
for item in current_navbar_items:
|
||||||
navbar_settings.append('help_dropdown', {
|
navbar_settings.append('help_dropdown', {
|
||||||
'item_label': item.item_label,
|
'item_label': item.item_label,
|
||||||
'item_type': item.item_type,
|
'item_type': item.item_type,
|
||||||
@ -161,5 +163,4 @@ def add_standard_navbar_items():
|
|||||||
navbar_settings.save()
|
navbar_settings.save()
|
||||||
|
|
||||||
def add_app_name():
|
def add_app_name():
|
||||||
settings = frappe.get_doc("System Settings")
|
frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
|
||||||
settings.app_name = _("ERPNext")
|
|
||||||
|
@ -10,13 +10,14 @@
|
|||||||
"hide_custom": 0,
|
"hide_custom": 0,
|
||||||
"icon": "getting-started",
|
"icon": "getting-started",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
|
"is_default": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Home",
|
"label": "Home",
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Healthcare",
|
"label": "Accounting",
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
@ -24,8 +25,8 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Patient",
|
"label": "Chart of Accounts",
|
||||||
"link_to": "Patient",
|
"link_to": "Account",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -34,25 +35,8 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Diagnosis",
|
"label": "Company",
|
||||||
"link_to": "Diagnosis",
|
"link_to": "Company",
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Agriculture",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Card Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Crop",
|
|
||||||
"link_to": "Crop",
|
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -61,8 +45,8 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Crop Cycle",
|
"label": "Customer",
|
||||||
"link_to": "Crop Cycle",
|
"link_to": "Customer",
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -71,112 +55,8 @@
|
|||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Location",
|
"label": "Supplier",
|
||||||
"link_to": "Location",
|
"link_to": "Supplier",
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Fertilizer",
|
|
||||||
"link_to": "Fertilizer",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Education",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Card Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Student",
|
|
||||||
"link_to": "Student",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Course",
|
|
||||||
"link_to": "Course",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Instructor",
|
|
||||||
"link_to": "Instructor",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Room",
|
|
||||||
"link_to": "Room",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Non Profit",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Card Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Member",
|
|
||||||
"link_to": "Member",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Volunteer",
|
|
||||||
"link_to": "Volunteer",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Chapter",
|
|
||||||
"link_to": "Chapter",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Donor",
|
|
||||||
"link_to": "Donor",
|
|
||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
@ -188,6 +68,16 @@
|
|||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Item",
|
||||||
|
"link_to": "Item",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -302,73 +192,6 @@
|
|||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Accounting",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Card Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Item",
|
|
||||||
"link_to": "Item",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Customer",
|
|
||||||
"link_to": "Customer",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Supplier",
|
|
||||||
"link_to": "Supplier",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Company",
|
|
||||||
"link_to": "Company",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Chart of Accounts",
|
|
||||||
"link_to": "Account",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dependencies": "",
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Opening Invoice Creation Tool",
|
|
||||||
"link_to": "Opening Invoice Creation Tool",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 1,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
@ -386,6 +209,16 @@
|
|||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Opening Invoice Creation Tool",
|
||||||
|
"link_to": "Opening Invoice Creation Tool",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dependencies": "",
|
"dependencies": "",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -415,9 +248,177 @@
|
|||||||
"link_type": "DocType",
|
"link_type": "DocType",
|
||||||
"onboard": 1,
|
"onboard": 1,
|
||||||
"type": "Link"
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Healthcare",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Card Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Patient",
|
||||||
|
"link_to": "Patient",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Diagnosis",
|
||||||
|
"link_to": "Diagnosis",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Education",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Card Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Student",
|
||||||
|
"link_to": "Student",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Instructor",
|
||||||
|
"link_to": "Instructor",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Course",
|
||||||
|
"link_to": "Course",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Room",
|
||||||
|
"link_to": "Room",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Non Profit",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Card Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Donor",
|
||||||
|
"link_to": "Donor",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Member",
|
||||||
|
"link_to": "Member",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Volunteer",
|
||||||
|
"link_to": "Volunteer",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Chapter",
|
||||||
|
"link_to": "Chapter",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Agriculture",
|
||||||
|
"onboard": 0,
|
||||||
|
"type": "Card Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Location",
|
||||||
|
"link_to": "Location",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Crop",
|
||||||
|
"link_to": "Crop",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Crop Cycle",
|
||||||
|
"link_to": "Crop Cycle",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dependencies": "",
|
||||||
|
"hidden": 0,
|
||||||
|
"is_query_report": 0,
|
||||||
|
"label": "Fertilizer",
|
||||||
|
"link_to": "Fertilizer",
|
||||||
|
"link_type": "DocType",
|
||||||
|
"onboard": 1,
|
||||||
|
"type": "Link"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2021-01-01 12:13:16.055668",
|
"modified": "2021-03-16 15:59:58.416154",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Home",
|
"name": "Home",
|
||||||
|
@ -717,6 +717,18 @@ $.extend(erpnext.item, {
|
|||||||
.on('focus', function(e) {
|
.on('focus', function(e) {
|
||||||
$(e.target).val('').trigger('input');
|
$(e.target).val('').trigger('input');
|
||||||
})
|
})
|
||||||
|
.on("awesomplete-open", () => {
|
||||||
|
let modal = field.$input.parents('.modal-dialog')[0];
|
||||||
|
if (modal) {
|
||||||
|
$(modal).removeClass("modal-dialog-scrollable");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("awesomplete-close", () => {
|
||||||
|
let modal = field.$input.parents('.modal-dialog')[0];
|
||||||
|
if (modal) {
|
||||||
|
$(modal).addClass("modal-dialog-scrollable");
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1054,6 +1054,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
|
||||||
"fieldname": "website_image_alt",
|
"fieldname": "website_image_alt",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Image Description"
|
"label": "Image Description"
|
||||||
@ -1066,7 +1067,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"max_attachments": 1,
|
"max_attachments": 1,
|
||||||
"modified": "2021-03-15 13:41:04.108932",
|
"modified": "2021-03-18 14:04:38.575519",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Item",
|
"name": "Item",
|
||||||
@ -1137,4 +1138,4 @@
|
|||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "item_name",
|
"title_field": "item_name",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,12 @@
|
|||||||
"attribute_name": "Test Size",
|
"attribute_name": "Test Size",
|
||||||
"priority": 1,
|
"priority": 1,
|
||||||
"item_attribute_values": [
|
"item_attribute_values": [
|
||||||
|
{"attribute_value": "Extra Small", "abbr": "XSL"},
|
||||||
{"attribute_value": "Small", "abbr": "S"},
|
{"attribute_value": "Small", "abbr": "S"},
|
||||||
{"attribute_value": "Medium", "abbr": "M"},
|
{"attribute_value": "Medium", "abbr": "M"},
|
||||||
{"attribute_value": "Large", "abbr": "L"},
|
{"attribute_value": "Large", "abbr": "L"},
|
||||||
{"attribute_value": "Extra Small", "abbr": "XSL"}
|
{"attribute_value": "Extra Large", "abbr": "XL"},
|
||||||
|
{"attribute_value": "2XL", "abbr": "2XL"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -25,8 +25,8 @@ class PickList(Document):
|
|||||||
if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
|
if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'):
|
||||||
continue
|
continue
|
||||||
if not item.serial_no:
|
if not item.serial_no:
|
||||||
frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}".format(
|
frappe.throw(_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
|
||||||
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse))),
|
frappe.bold(item.idx), frappe.bold(item.item_code), frappe.bold(item.warehouse)),
|
||||||
title=_("Serial Nos Required"))
|
title=_("Serial Nos Required"))
|
||||||
if len(item.serial_no.split('\n')) == item.picked_qty:
|
if len(item.serial_no.split('\n')) == item.picked_qty:
|
||||||
continue
|
continue
|
||||||
@ -380,7 +380,7 @@ def create_stock_entry(pick_list):
|
|||||||
|
|
||||||
stock_entry.set_incoming_rate()
|
stock_entry.set_incoming_rate()
|
||||||
stock_entry.set_actual_qty()
|
stock_entry.set_actual_qty()
|
||||||
stock_entry.calculate_rate_and_amount(update_finished_item_rate=False)
|
stock_entry.calculate_rate_and_amount()
|
||||||
|
|
||||||
return stock_entry.as_dict()
|
return stock_entry.as_dict()
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import cint, get_link_to_form
|
from frappe.utils import cint, get_link_to_form, add_to_date, today
|
||||||
from erpnext.stock.stock_ledger import repost_future_sle
|
from erpnext.stock.stock_ledger import repost_future_sle
|
||||||
from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
|
from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
|
||||||
from frappe.utils.user import get_users_with_role
|
from frappe.utils.user import get_users_with_role
|
||||||
@ -29,7 +29,7 @@ class RepostItemValuation(Document):
|
|||||||
self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
|
self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
|
||||||
elif self.warehouse:
|
elif self.warehouse:
|
||||||
self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
|
self.company = frappe.get_cached_value("Warehouse", self.warehouse, "company")
|
||||||
|
|
||||||
def set_status(self, status=None):
|
def set_status(self, status=None):
|
||||||
if not status:
|
if not status:
|
||||||
status = 'Queued'
|
status = 'Queued'
|
||||||
@ -54,7 +54,6 @@ def repost(doc):
|
|||||||
|
|
||||||
repost_sl_entries(doc)
|
repost_sl_entries(doc)
|
||||||
repost_gl_entries(doc)
|
repost_gl_entries(doc)
|
||||||
check_if_stock_and_account_balance_synced(doc.posting_date, doc.company)
|
|
||||||
|
|
||||||
doc.set_status('Completed')
|
doc.set_status('Completed')
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -103,7 +102,7 @@ def notify_error_to_stock_managers(doc, traceback):
|
|||||||
recipients = get_users_with_role("Stock Manager")
|
recipients = get_users_with_role("Stock Manager")
|
||||||
if not recipients:
|
if not recipients:
|
||||||
get_users_with_role("System Manager")
|
get_users_with_role("System Manager")
|
||||||
|
|
||||||
subject = _("Error while reposting item valuation")
|
subject = _("Error while reposting item valuation")
|
||||||
message = (_("Hi,") + "<br>"
|
message = (_("Hi,") + "<br>"
|
||||||
+ _("An error has been appeared while reposting item valuation via {0}")
|
+ _("An error has been appeared while reposting item valuation via {0}")
|
||||||
@ -112,4 +111,24 @@ def notify_error_to_stock_managers(doc, traceback):
|
|||||||
)
|
)
|
||||||
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
frappe.sendmail(recipients=recipients, subject=subject, message=message)
|
||||||
|
|
||||||
|
def repost_entries():
|
||||||
|
riv_entries = get_repost_item_valuation_entries()
|
||||||
|
|
||||||
|
for row in riv_entries:
|
||||||
|
doc = frappe.get_cached_doc('Repost Item Valuation', row.name)
|
||||||
|
repost(doc)
|
||||||
|
|
||||||
|
riv_entries = get_repost_item_valuation_entries()
|
||||||
|
if riv_entries:
|
||||||
|
return
|
||||||
|
|
||||||
|
for d in frappe.get_all('Company', filters= {'enable_perpetual_inventory': 1}):
|
||||||
|
check_if_stock_and_account_balance_synced(today(), d.company)
|
||||||
|
|
||||||
|
def get_repost_item_valuation_entries():
|
||||||
|
date = add_to_date(today(), hours=-12)
|
||||||
|
|
||||||
|
return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation`
|
||||||
|
WHERE status != 'Completed' and creation <= %s and docstatus = 1
|
||||||
|
ORDER BY timestamp(posting_date, posting_time) asc, creation asc
|
||||||
|
""", date, as_dict=1)
|
@ -458,7 +458,7 @@ class StockEntry(StockController):
|
|||||||
Set rate for outgoing, scrapped and finished items
|
Set rate for outgoing, scrapped and finished items
|
||||||
"""
|
"""
|
||||||
# Set rate for outgoing items
|
# Set rate for outgoing items
|
||||||
outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate)
|
outgoing_items_cost = self.set_rate_for_outgoing_items(reset_outgoing_rate, raise_error_if_no_rate)
|
||||||
finished_item_qty = sum([d.transfer_qty for d in self.items if d.is_finished_item])
|
finished_item_qty = sum([d.transfer_qty for d in self.items if d.is_finished_item])
|
||||||
|
|
||||||
# Set basic rate for incoming items
|
# Set basic rate for incoming items
|
||||||
@ -482,13 +482,13 @@ class StockEntry(StockController):
|
|||||||
d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
|
d.basic_rate = flt(d.basic_rate, d.precision("basic_rate"))
|
||||||
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
|
||||||
|
|
||||||
def set_rate_for_outgoing_items(self, reset_outgoing_rate=True):
|
def set_rate_for_outgoing_items(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
|
||||||
outgoing_items_cost = 0.0
|
outgoing_items_cost = 0.0
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if d.s_warehouse:
|
if d.s_warehouse:
|
||||||
if reset_outgoing_rate:
|
if reset_outgoing_rate:
|
||||||
args = self.get_args_for_incoming_rate(d)
|
args = self.get_args_for_incoming_rate(d)
|
||||||
rate = get_incoming_rate(args)
|
rate = get_incoming_rate(args, raise_error_if_no_rate)
|
||||||
if rate > 0:
|
if rate > 0:
|
||||||
d.basic_rate = rate
|
d.basic_rate = rate
|
||||||
|
|
||||||
@ -1010,7 +1010,8 @@ class StockEntry(StockController):
|
|||||||
|
|
||||||
self.set_scrap_items()
|
self.set_scrap_items()
|
||||||
self.set_actual_qty()
|
self.set_actual_qty()
|
||||||
self.calculate_rate_and_amount(raise_error_if_no_rate=False)
|
self.validate_customer_provided_item()
|
||||||
|
self.calculate_rate_and_amount()
|
||||||
|
|
||||||
def set_scrap_items(self):
|
def set_scrap_items(self):
|
||||||
if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]:
|
if self.purpose != "Send to Subcontractor" and self.purpose in ["Manufacture", "Repack"]:
|
||||||
|
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