Merge branch 'develop' into UAE-VAT-Format
This commit is contained in:
commit
abf628c95d
@ -195,9 +195,7 @@ def create_sales_invoice_record(qty=1):
|
||||
|
||||
def create_records():
|
||||
# create a new loyalty Account
|
||||
if frappe.db.exists("Account", "Loyalty - _TC"):
|
||||
return
|
||||
|
||||
if not frappe.db.exists("Account", "Loyalty - _TC"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Account",
|
||||
"account_name": "Loyalty",
|
||||
@ -208,6 +206,7 @@ def create_records():
|
||||
}).insert()
|
||||
|
||||
# create a new loyalty program Single tier
|
||||
if not frappe.db.exists("Loyalty Program","Test Single Loyalty"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Loyalty Program",
|
||||
"loyalty_program_name": "Test Single Loyalty",
|
||||
@ -227,6 +226,7 @@ def create_records():
|
||||
}).insert()
|
||||
|
||||
# create a new customer
|
||||
if not frappe.db.exists("Customer","Test Loyalty Customer"):
|
||||
frappe.get_doc({
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "Test Loyalty Customer",
|
||||
@ -236,6 +236,7 @@ def create_records():
|
||||
}).insert()
|
||||
|
||||
# create a new loyalty program Multiple tier
|
||||
if not frappe.db.exists("Loyalty Program","Test Multiple Loyalty"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Loyalty Program",
|
||||
"loyalty_program_name": "Test Multiple Loyalty",
|
||||
@ -262,7 +263,8 @@ def create_records():
|
||||
}).insert()
|
||||
|
||||
# create an item
|
||||
item = frappe.get_doc({
|
||||
if not frappe.db.exists("Item", "Loyal Item"):
|
||||
frappe.get_doc({
|
||||
"doctype": "Item",
|
||||
"item_code": "Loyal Item",
|
||||
"item_name": "Loyal Item",
|
||||
@ -274,9 +276,10 @@ def create_records():
|
||||
}).insert()
|
||||
|
||||
# create item price
|
||||
if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
|
||||
frappe.get_doc({
|
||||
"doctype": "Item Price",
|
||||
"price_list": "Standard Selling",
|
||||
"item_code": item.item_code,
|
||||
"item_code": "Loyal Item",
|
||||
"price_list_rate": 10000
|
||||
}).insert()
|
||||
|
@ -45,7 +45,7 @@ class TestPOSClosingEntry(unittest.TestCase):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
|
||||
def init_user_and_profile():
|
||||
def init_user_and_profile(**args):
|
||||
user = 'test@example.com'
|
||||
test_user = frappe.get_doc('User', user)
|
||||
|
||||
@ -53,7 +53,7 @@ def init_user_and_profile():
|
||||
test_user.add_roles(*roles)
|
||||
frappe.set_user(user)
|
||||
|
||||
pos_profile = make_pos_profile()
|
||||
pos_profile = make_pos_profile(**args)
|
||||
pos_profile.append('applicable_for_users', {
|
||||
'default': 1,
|
||||
'user': user
|
||||
|
@ -139,7 +139,8 @@ class POSInvoice(SalesInvoice):
|
||||
frappe.throw(_("At least one mode of payment is required for POS invoice."))
|
||||
|
||||
def validate_change_account(self):
|
||||
if frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company:
|
||||
if self.change_amount and self.account_for_change_amount and \
|
||||
frappe.db.get_value("Account", self.account_for_change_amount, "company") != self.company:
|
||||
frappe.throw(_("The selected change account {} doesn't belongs to Company {}.").format(self.account_for_change_amount, self.company))
|
||||
|
||||
def validate_change_amount(self):
|
||||
|
@ -7,6 +7,8 @@ import frappe
|
||||
import unittest, copy, time
|
||||
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
|
||||
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.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
|
||||
class TestPOSInvoice(unittest.TestCase):
|
||||
def test_timestamp_change(self):
|
||||
@ -221,29 +223,29 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
se = make_serialized_item(company='_Test Company with perpetual inventory',
|
||||
target_warehouse="Stores - TCP1", cost_center='Main - TCP1', expense_account='Cost of Goods Sold - TCP1')
|
||||
se = make_serialized_item(company='_Test Company',
|
||||
target_warehouse="Stores - _TC", cost_center='Main - _TC', expense_account='Cost of Goods Sold - _TC')
|
||||
|
||||
serial_nos = get_serial_nos(se.get("items")[0].serial_no)
|
||||
|
||||
pos = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
|
||||
account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
|
||||
expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
|
||||
pos = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
|
||||
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
|
||||
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
|
||||
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
||||
|
||||
pos.get("items")[0].serial_no = serial_nos[0]
|
||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
|
||||
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
||||
|
||||
pos.insert()
|
||||
pos.submit()
|
||||
|
||||
pos2 = create_pos_invoice(company='_Test Company with perpetual inventory', debit_to='Debtors - TCP1',
|
||||
account_for_change_amount='Cash - TCP1', warehouse='Stores - TCP1', income_account='Sales - TCP1',
|
||||
expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1',
|
||||
pos2 = create_pos_invoice(company='_Test Company', debit_to='Debtors - _TC',
|
||||
account_for_change_amount='Cash - _TC', warehouse='Stores - _TC', income_account='Sales - _TC',
|
||||
expense_account='Cost of Goods Sold - _TC', cost_center='Main - _TC',
|
||||
item=se.get("items")[0].item_code, rate=1000, do_not_save=1)
|
||||
|
||||
pos2.get("items")[0].serial_no = serial_nos[0]
|
||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - TCP1', 'amount': 1000})
|
||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pos2.insert)
|
||||
|
||||
@ -294,7 +296,7 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(rate=300, additional_discount_percentage=10, do_not_submit=1)
|
||||
pos_inv.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 270
|
||||
})
|
||||
pos_inv.submit()
|
||||
|
||||
@ -307,8 +309,9 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
merge_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
|
||||
self.assertEqual(sales_invoice.grand_total, 3500)
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 3470)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
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
|
||||
@ -348,8 +351,55 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
merge_pos_invoices()
|
||||
|
||||
pos_inv.load_from_db()
|
||||
sales_invoice = frappe.get_doc("Sales Invoice", pos_inv.consolidated_invoice)
|
||||
self.assertEqual(sales_invoice.rounded_total, 840)
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
|
||||
self.assertEqual(rounded_total, 840)
|
||||
frappe.set_user("Administrator")
|
||||
|
||||
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_invoice_merge_log.pos_invoice_merge_log import merge_pos_invoices
|
||||
|
||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||
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)
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
test_user, pos_profile = init_user_and_profile()
|
||||
pos_inv = create_pos_invoice(rate=300, do_not_submit=1)
|
||||
pos_inv.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 300
|
||||
})
|
||||
pos_inv.append('taxes', {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Service Tax",
|
||||
"rate": 14,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
self.assertRaises(frappe.ValidationError, pos_inv.submit)
|
||||
|
||||
pos_inv2 = create_pos_invoice(rate=400, do_not_submit=1)
|
||||
pos_inv2.append('payments', {
|
||||
'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 400
|
||||
})
|
||||
pos_inv2.append('taxes', {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "_Test Account Service Tax - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "Service Tax",
|
||||
"rate": 14,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
pos_inv2.submit()
|
||||
|
||||
merge_pos_invoices()
|
||||
|
||||
pos_inv2.load_from_db()
|
||||
rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
|
||||
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):
|
||||
args = frappe._dict(args)
|
||||
@ -364,8 +414,6 @@ def create_pos_invoice(**args):
|
||||
pos_inv.is_pos = 1
|
||||
pos_inv.pos_profile = args.pos_profile or pos_profile.name
|
||||
|
||||
pos_inv.set_missing_values()
|
||||
|
||||
if args.posting_date:
|
||||
pos_inv.set_posting_time = 1
|
||||
pos_inv.posting_date = args.posting_date or frappe.utils.nowdate()
|
||||
@ -379,6 +427,8 @@ def create_pos_invoice(**args):
|
||||
pos_inv.conversion_rate = args.conversion_rate or 1
|
||||
pos_inv.account_for_change_amount = args.account_for_change_amount or "Cash - _TC"
|
||||
|
||||
pos_inv.set_missing_values()
|
||||
|
||||
pos_inv.append("items", {
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
|
@ -15,15 +15,6 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
|
||||
frm.call({
|
||||
method: "erpnext.accounts.doctype.pos_profile.pos_profile.get_series",
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
set_field_options("naming_series", r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
frappe.ui.form.on('POS Profile', {
|
||||
|
@ -8,7 +8,6 @@
|
||||
"field_order": [
|
||||
"disabled",
|
||||
"section_break_2",
|
||||
"naming_series",
|
||||
"customer",
|
||||
"company",
|
||||
"country",
|
||||
@ -59,17 +58,6 @@
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Series",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "[Select]",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
@ -323,7 +311,7 @@
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-29 12:20:30.977272",
|
||||
"modified": "2020-10-01 17:29:27.759088",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
@ -109,10 +109,6 @@ def get_child_nodes(group_type, root):
|
||||
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
|
||||
lft >= {lft} and rgt <= {rgt} order by lft""".format(tab=group_type, lft=lft, rgt=rgt), as_dict=1)
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_series():
|
||||
return frappe.get_meta("POS Invoice").get_field("naming_series").options or "s"
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
|
@ -711,7 +711,8 @@ class PurchaseInvoice(BuyingController):
|
||||
item.item_tax_amount / self.conversion_rate)
|
||||
}, item=item))
|
||||
else:
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||
asset_category=item.asset_category,company=self.company)
|
||||
|
||||
cwip_account_currency = get_account_currency(cwip_account)
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
|
@ -1002,7 +1002,8 @@ def make_purchase_invoice(**args):
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"project": args.project,
|
||||
"rejected_warehouse": args.rejected_warehouse or "",
|
||||
"rejected_serial_no": args.rejected_serial_no or ""
|
||||
"rejected_serial_no": args.rejected_serial_no or "",
|
||||
"asset_location": args.location or ""
|
||||
})
|
||||
|
||||
if args.get_taxes_and_charges:
|
||||
|
@ -19,6 +19,7 @@
|
||||
"is_return",
|
||||
"column_break1",
|
||||
"company",
|
||||
"company_tax_id",
|
||||
"posting_date",
|
||||
"posting_time",
|
||||
"set_posting_time",
|
||||
@ -1926,6 +1927,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:(doc.is_pos && doc.is_consolidated)",
|
||||
"fieldname": "is_consolidated",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Consolidated",
|
||||
@ -1940,6 +1942,13 @@
|
||||
"hide_seconds": 1,
|
||||
"label": "Is Internal Customer",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "company.tax_id",
|
||||
"fieldname": "company_tax_id",
|
||||
"fieldtype": "Data",
|
||||
"label": "Company Tax ID",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
|
@ -428,7 +428,7 @@ class SalesInvoice(SellingController):
|
||||
if pos.get('account_for_change_amount'):
|
||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||
|
||||
for fieldname in ('naming_series', 'currency', 'letter_head', 'tc_name',
|
||||
for fieldname in ('currency', 'letter_head', 'tc_name',
|
||||
'company', 'select_print_heading', 'write_off_account', 'taxes_and_charges',
|
||||
'write_off_cost_center', 'apply_discount_on', 'cost_center'):
|
||||
if (not for_validate) or (for_validate and not self.get(fieldname)):
|
||||
@ -572,7 +572,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def validate_pos(self):
|
||||
if self.is_return:
|
||||
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) > \
|
||||
invoice_total = self.rounded_total or self.grand_total
|
||||
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(invoice_total) > \
|
||||
1.0/(10.0**(self.precision("grand_total") + 1.0)):
|
||||
frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
|
||||
|
||||
|
@ -345,6 +345,7 @@ class Subscription(Document):
|
||||
invoice.set_taxes()
|
||||
|
||||
# Due date
|
||||
if self.days_until_due:
|
||||
invoice.append(
|
||||
'payment_schedule',
|
||||
{
|
||||
|
@ -106,6 +106,7 @@ def get_tds_amount(suppliers, net_total, company, tax_details, fiscal_year_detai
|
||||
from `tabGL Entry`
|
||||
where company = %s and
|
||||
party in %s and fiscal_year=%s and credit > 0
|
||||
and is_opening = 'No'
|
||||
""", (company, tuple(suppliers), fiscal_year), as_dict=1)
|
||||
|
||||
vouchers = [d.voucher_no for d in entries]
|
||||
@ -192,6 +193,7 @@ def get_advance_vouchers(suppliers, fiscal_year=None, company=None, from_date=No
|
||||
select distinct voucher_no
|
||||
from `tabGL Entry`
|
||||
where party in %s and %s and debit > 0
|
||||
and is_opening = 'No'
|
||||
""", (tuple(suppliers), condition)) or []
|
||||
|
||||
def get_debit_note_amount(suppliers, year_start_date, year_end_date, company=None):
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
frappe.query_reports["Bank Reconciliation Statement"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"account",
|
||||
"label": __("Bank Account"),
|
||||
@ -12,11 +20,14 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
|
||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "",
|
||||
"reqd": 1,
|
||||
"get_query": function() {
|
||||
var company = frappe.query_report.get_filter_value('company')
|
||||
return {
|
||||
"query": "erpnext.controllers.queries.get_account_list",
|
||||
"filters": [
|
||||
['Account', 'account_type', 'in', 'Bank, Cash'],
|
||||
['Account', 'is_group', '=', 0],
|
||||
['Account', 'disabled', '=', 0],
|
||||
['Account', 'company', '=', company],
|
||||
]
|
||||
}
|
||||
}
|
||||
|
76
erpnext/accounts/report/pos_register/pos_register.js
Normal file
76
erpnext/accounts/report/pos_register/pos_register.js
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["POS Register"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
|
||||
"reqd": 1,
|
||||
"width": "60px"
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today(),
|
||||
"reqd": 1,
|
||||
"width": "60px"
|
||||
},
|
||||
{
|
||||
"fieldname":"pos_profile",
|
||||
"label": __("POS Profile"),
|
||||
"fieldtype": "Link",
|
||||
"options": "POS Profile"
|
||||
},
|
||||
{
|
||||
"fieldname":"cashier",
|
||||
"label": __("Cashier"),
|
||||
"fieldtype": "Link",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname":"customer",
|
||||
"label": __("Customer"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
"fieldname":"mode_of_payment",
|
||||
"label": __("Payment Method"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Mode of Payment"
|
||||
},
|
||||
{
|
||||
"fieldname":"group_by",
|
||||
"label": __("Group by"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["", "POS Profile", "Cashier", "Payment Method", "Customer"],
|
||||
"default": "POS Profile"
|
||||
},
|
||||
{
|
||||
"fieldname":"is_return",
|
||||
"label": __("Is Return"),
|
||||
"fieldtype": "Check"
|
||||
},
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (data && data.bold) {
|
||||
value = value.bold();
|
||||
|
||||
}
|
||||
return value;
|
||||
}
|
||||
};
|
30
erpnext/accounts/report/pos_register/pos_register.json
Normal file
30
erpnext/accounts/report/pos_register/pos_register.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2020-09-10 19:25:03.766871",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"json": "{}",
|
||||
"modified": "2020-09-10 19:25:15.851331",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Register",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "POS Invoice",
|
||||
"report_name": "POS Register",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"role": "Accounts User"
|
||||
}
|
||||
]
|
||||
}
|
222
erpnext/accounts/report/pos_register/pos_register.py
Normal file
222
erpnext/accounts/report/pos_register/pos_register.py
Normal file
@ -0,0 +1,222 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, _dict
|
||||
from erpnext import get_company_currency, get_default_company
|
||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
return [], []
|
||||
|
||||
validate_filters(filters)
|
||||
|
||||
columns = get_columns(filters)
|
||||
|
||||
group_by_field = get_group_by_field(filters.get("group_by"))
|
||||
|
||||
pos_entries = get_pos_entries(filters, group_by_field)
|
||||
if group_by_field != "mode_of_payment":
|
||||
concat_mode_of_payments(pos_entries)
|
||||
|
||||
# return only entries if group by is unselected
|
||||
if not group_by_field:
|
||||
return columns, pos_entries
|
||||
|
||||
# handle grouping
|
||||
invoice_map, grouped_data = {}, []
|
||||
for d in pos_entries:
|
||||
invoice_map.setdefault(d[group_by_field], []).append(d)
|
||||
|
||||
for key in invoice_map:
|
||||
invoices = invoice_map[key]
|
||||
grouped_data += invoices
|
||||
add_subtotal_row(grouped_data, invoices, group_by_field, key)
|
||||
|
||||
# move group by column to first position
|
||||
column_index = next((index for (index, d) in enumerate(columns) if d["fieldname"] == group_by_field), None)
|
||||
columns.insert(0, columns.pop(column_index))
|
||||
|
||||
return columns, grouped_data
|
||||
|
||||
def get_pos_entries(filters, group_by_field):
|
||||
conditions = get_conditions(filters)
|
||||
order_by = "p.posting_date"
|
||||
select_mop_field, from_sales_invoice_payment, group_by_mop_condition = "", "", ""
|
||||
if group_by_field == "mode_of_payment":
|
||||
select_mop_field = ", sip.mode_of_payment"
|
||||
from_sales_invoice_payment = ", `tabSales Invoice Payment` sip"
|
||||
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount, 0) != 0 AND"
|
||||
order_by += ", sip.mode_of_payment"
|
||||
|
||||
elif group_by_field:
|
||||
order_by += ", p.{}".format(group_by_field)
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
SELECT
|
||||
p.posting_date, p.name as pos_invoice, p.pos_profile,
|
||||
p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount,
|
||||
p.customer, p.is_return {select_mop_field}
|
||||
FROM
|
||||
`tabPOS Invoice` p {from_sales_invoice_payment}
|
||||
WHERE
|
||||
{group_by_mop_condition}
|
||||
{conditions}
|
||||
ORDER BY
|
||||
{order_by}
|
||||
""".format(
|
||||
select_mop_field=select_mop_field,
|
||||
from_sales_invoice_payment=from_sales_invoice_payment,
|
||||
group_by_mop_condition=group_by_mop_condition,
|
||||
conditions=conditions,
|
||||
order_by=order_by
|
||||
), filters, as_dict=1)
|
||||
|
||||
def concat_mode_of_payments(pos_entries):
|
||||
mode_of_payments = get_mode_of_payments(set([d.pos_invoice for d in pos_entries]))
|
||||
for entry in pos_entries:
|
||||
if mode_of_payments.get(entry.pos_invoice):
|
||||
entry.mode_of_payment = ", ".join(mode_of_payments.get(entry.pos_invoice, []))
|
||||
|
||||
def add_subtotal_row(data, group_invoices, group_by_field, group_by_value):
|
||||
grand_total = sum([d.grand_total for d in group_invoices])
|
||||
paid_amount = sum([d.paid_amount for d in group_invoices])
|
||||
data.append({
|
||||
group_by_field: group_by_value,
|
||||
"grand_total": grand_total,
|
||||
"paid_amount": paid_amount,
|
||||
"bold": 1
|
||||
})
|
||||
data.append({})
|
||||
|
||||
def validate_filters(filters):
|
||||
if not filters.get("company"):
|
||||
frappe.throw(_("{0} is mandatory").format(_("Company")))
|
||||
|
||||
if not filters.get("from_date") and not filters.get("to_date"):
|
||||
frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date"))))
|
||||
|
||||
if filters.from_date > filters.to_date:
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')):
|
||||
frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile"))
|
||||
|
||||
if (filters.get("customer") and filters.get("group_by") == _('Customer')):
|
||||
frappe.throw(_("Can not filter based on Customer, if grouped by Customer"))
|
||||
|
||||
if (filters.get("owner") and filters.get("group_by") == _('Cashier')):
|
||||
frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier"))
|
||||
|
||||
if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')):
|
||||
frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
|
||||
company=filters.get("company"),
|
||||
from_date=filters.get("from_date"),
|
||||
to_date=filters.get("to_date"))
|
||||
|
||||
if filters.get("pos_profile"):
|
||||
conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
|
||||
|
||||
if filters.get("owner"):
|
||||
conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
|
||||
|
||||
if filters.get("customer"):
|
||||
conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
|
||||
|
||||
if filters.get("is_return"):
|
||||
conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
|
||||
|
||||
if filters.get("mode_of_payment"):
|
||||
conditions += """
|
||||
AND EXISTS(
|
||||
SELECT name FROM `tabSales Invoice Payment` sip
|
||||
WHERE parent=p.name AND ifnull(sip.mode_of_payment, '') = %(mode_of_payment)s
|
||||
)"""
|
||||
|
||||
return conditions
|
||||
|
||||
def get_group_by_field(group_by):
|
||||
group_by_field = ""
|
||||
|
||||
if group_by == "POS Profile":
|
||||
group_by_field = "pos_profile"
|
||||
elif group_by == "Cashier":
|
||||
group_by_field = "owner"
|
||||
elif group_by == "Customer":
|
||||
group_by_field = "customer"
|
||||
elif group_by == "Payment Method":
|
||||
group_by_field = "mode_of_payment"
|
||||
|
||||
return group_by_field
|
||||
|
||||
def get_columns(filters):
|
||||
columns = [
|
||||
{
|
||||
"label": _("Posting Date"),
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"label": _("POS Invoice"),
|
||||
"fieldname": "pos_invoice",
|
||||
"fieldtype": "Link",
|
||||
"options": "POS Invoice",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Customer"),
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("POS Profile"),
|
||||
"fieldname": "pos_profile",
|
||||
"fieldtype": "Link",
|
||||
"options": "POS Profile",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _("Cashier"),
|
||||
"fieldname": "owner",
|
||||
"fieldtype": "Link",
|
||||
"options": "User",
|
||||
"width": 140
|
||||
},
|
||||
{
|
||||
"label": _("Grand Total"),
|
||||
"fieldname": "grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Paid Amount"),
|
||||
"fieldname": "paid_amount",
|
||||
"fieldtype": "Currency",
|
||||
"options": "company:currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"label": _("Payment Method"),
|
||||
"fieldname": "mode_of_payment",
|
||||
"fieldtype": "Data",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"label": _("Is Return"),
|
||||
"fieldname": "is_return",
|
||||
"fieldtype": "Data",
|
||||
"width": 80
|
||||
},
|
||||
]
|
||||
|
||||
return columns
|
@ -466,29 +466,37 @@ class Asset(AccountsController):
|
||||
|
||||
def validate_make_gl_entry(self):
|
||||
purchase_document = self.get_purchase_document()
|
||||
asset_bought_with_invoice = purchase_document == self.purchase_invoice
|
||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||
# check if expense already has been booked in case of cwip was enabled after purchasing asset
|
||||
expense_booked = False
|
||||
cwip_booked = False
|
||||
|
||||
if asset_bought_with_invoice:
|
||||
expense_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||
(purchase_document, fixed_asset_account), as_dict=1)
|
||||
else:
|
||||
cwip_booked = frappe.db.sql("""SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s""",
|
||||
(purchase_document, cwip_account), as_dict=1)
|
||||
|
||||
if cwip_enabled and (expense_booked or not cwip_booked):
|
||||
# if expense has already booked from invoice or cwip is booked from receipt
|
||||
if not purchase_document:
|
||||
return False
|
||||
elif not cwip_enabled and (not expense_booked or cwip_booked):
|
||||
# if cwip is disabled but expense hasn't been booked yet
|
||||
return True
|
||||
elif cwip_enabled:
|
||||
# default condition
|
||||
|
||||
asset_bought_with_invoice = (purchase_document == self.purchase_invoice)
|
||||
fixed_asset_account = self.get_fixed_asset_account()
|
||||
|
||||
cwip_enabled = is_cwip_accounting_enabled(self.asset_category)
|
||||
cwip_account = self.get_cwip_account(cwip_enabled=cwip_enabled)
|
||||
|
||||
query = """SELECT name FROM `tabGL Entry` WHERE voucher_no = %s and account = %s"""
|
||||
if asset_bought_with_invoice:
|
||||
# with invoice purchase either expense or cwip has been booked
|
||||
expense_booked = frappe.db.sql(query, (purchase_document, fixed_asset_account), as_dict=1)
|
||||
if expense_booked:
|
||||
# if expense is already booked from invoice then do not make gl entries regardless of cwip enabled/disabled
|
||||
return False
|
||||
|
||||
cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
|
||||
if cwip_booked:
|
||||
# if cwip is booked from invoice then make gl entries regardless of cwip enabled/disabled
|
||||
return True
|
||||
else:
|
||||
# with receipt purchase either cwip has been booked or no entries have been made
|
||||
if not cwip_account:
|
||||
# if cwip account isn't available do not make gl entries
|
||||
return False
|
||||
|
||||
cwip_booked = frappe.db.sql(query, (purchase_document, cwip_account), as_dict=1)
|
||||
# if cwip is not booked from receipt then do not make gl entries
|
||||
# if cwip is booked from receipt then make gl entries
|
||||
return cwip_booked
|
||||
|
||||
def get_purchase_document(self):
|
||||
asset_bought_with_invoice = self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')
|
||||
@ -496,20 +504,25 @@ class Asset(AccountsController):
|
||||
|
||||
return purchase_document
|
||||
|
||||
def get_asset_accounts(self):
|
||||
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
|
||||
asset_category = self.asset_category, company = self.company)
|
||||
def get_fixed_asset_account(self):
|
||||
return get_asset_category_account('fixed_asset_account', None, self.name, None, self.asset_category, self.company)
|
||||
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||
self.name, self.asset_category, self.company)
|
||||
def get_cwip_account(self, cwip_enabled=False):
|
||||
cwip_account = None
|
||||
try:
|
||||
cwip_account = get_asset_account("capital_work_in_progress_account", self.name, self.asset_category, self.company)
|
||||
except:
|
||||
# if no cwip account found in category or company and "cwip is enabled" then raise else silently pass
|
||||
if cwip_enabled:
|
||||
raise
|
||||
|
||||
return fixed_asset_account, cwip_account
|
||||
return cwip_account
|
||||
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
|
||||
purchase_document = self.get_purchase_document()
|
||||
fixed_asset_account, cwip_account = self.get_asset_accounts()
|
||||
fixed_asset_account, cwip_account = self.get_fixed_asset_account(), self.get_cwip_account()
|
||||
|
||||
if (purchase_document and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
|
||||
|
||||
@ -561,14 +574,18 @@ class Asset(AccountsController):
|
||||
return 100 * (1 - flt(depreciation_rate, float_precision))
|
||||
|
||||
def update_maintenance_status():
|
||||
assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1})
|
||||
assets = frappe.get_all(
|
||||
"Asset", filters={"docstatus": 1, "maintenance_required": 1}
|
||||
)
|
||||
|
||||
for asset in assets:
|
||||
asset = frappe.get_doc("Asset", asset.name)
|
||||
if frappe.db.exists('Asset Maintenance Task', {'parent': asset.name, 'next_due_date': today()}):
|
||||
asset.set_status('In Maintenance')
|
||||
if frappe.db.exists('Asset Repair', {'asset_name': asset.name, 'repair_status': 'Pending'}):
|
||||
asset.set_status('Out of Order')
|
||||
if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}):
|
||||
asset.set_status("Out of Order")
|
||||
elif frappe.db.exists("Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()}):
|
||||
asset.set_status("In Maintenance")
|
||||
else:
|
||||
asset.set_status()
|
||||
|
||||
def make_post_gl_entry():
|
||||
|
||||
|
@ -9,6 +9,7 @@ from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, ad
|
||||
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
|
||||
from erpnext.assets.doctype.asset.asset import make_sales_invoice
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
|
||||
|
||||
class TestAsset(unittest.TestCase):
|
||||
@ -558,81 +559,6 @@ class TestAsset(unittest.TestCase):
|
||||
|
||||
self.assertEqual(gle, expected_gle)
|
||||
|
||||
def test_gle_with_cwip_toggling(self):
|
||||
# TEST: purchase an asset with cwip enabled and then disable cwip and try submitting the asset
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=5000, do_not_submit=True, location="Test Location")
|
||||
pr.set('taxes', [{
|
||||
'category': 'Total',
|
||||
'add_deduct_tax': 'Add',
|
||||
'charge_type': 'On Net Total',
|
||||
'account_head': '_Test Account Service Tax - _TC',
|
||||
'description': '_Test Account Service Tax',
|
||||
'cost_center': 'Main - _TC',
|
||||
'rate': 5.0
|
||||
}, {
|
||||
'category': 'Valuation and Total',
|
||||
'add_deduct_tax': 'Add',
|
||||
'charge_type': 'On Net Total',
|
||||
'account_head': '_Test Account Shipping Charges - _TC',
|
||||
'description': '_Test Account Shipping Charges',
|
||||
'cost_center': 'Main - _TC',
|
||||
'rate': 5.0
|
||||
}])
|
||||
pr.submit()
|
||||
expected_gle = (
|
||||
("Asset Received But Not Billed - _TC", 0.0, 5250.0),
|
||||
("CWIP Account - _TC", 5250.0, 0.0)
|
||||
)
|
||||
pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Purchase Receipt' and voucher_no = %s
|
||||
order by account""", pr.name)
|
||||
self.assertEqual(pr_gle, expected_gle)
|
||||
|
||||
pi = make_invoice(pr.name)
|
||||
pi.submit()
|
||||
expected_gle = (
|
||||
("_Test Account Service Tax - _TC", 250.0, 0.0),
|
||||
("_Test Account Shipping Charges - _TC", 250.0, 0.0),
|
||||
("Asset Received But Not Billed - _TC", 5250.0, 0.0),
|
||||
("Creditors - _TC", 0.0, 5500.0),
|
||||
("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
|
||||
)
|
||||
pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Purchase Invoice' and voucher_no = %s
|
||||
order by account""", pi.name)
|
||||
self.assertEqual(pi_gle, expected_gle)
|
||||
|
||||
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
month_end_date = get_last_day(nowdate())
|
||||
asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
|
||||
self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
|
||||
asset_doc.append("finance_books", {
|
||||
"expected_value_after_useful_life": 200,
|
||||
"depreciation_method": "Straight Line",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 10,
|
||||
"depreciation_start_date": month_end_date
|
||||
})
|
||||
|
||||
# disable cwip and try submitting
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||
asset_doc.submit()
|
||||
# asset should have gl entries even if cwip is disabled
|
||||
expected_gle = (
|
||||
("_Test Fixed Asset - _TC", 5250.0, 0.0),
|
||||
("CWIP Account - _TC", 0.0, 5250.0)
|
||||
)
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
where voucher_type='Asset' and voucher_no = %s
|
||||
order by account""", asset_doc.name)
|
||||
self.assertEqual(gle, expected_gle)
|
||||
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
|
||||
def test_expense_head(self):
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=2, rate=200000.0, location="Test Location")
|
||||
@ -641,6 +567,74 @@ class TestAsset(unittest.TestCase):
|
||||
|
||||
self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
|
||||
|
||||
def test_asset_cwip_toggling_cases(self):
|
||||
cwip = frappe.db.get_value("Asset Category", "Computers", "enable_cwip_accounting")
|
||||
name = frappe.db.get_value("Asset Category Account", filters={"parent": "Computers"}, fieldname=["name"])
|
||||
cwip_acc = "CWIP Account - _TC"
|
||||
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", "")
|
||||
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", "")
|
||||
|
||||
# case 0 -- PI with cwip disable, Asset with cwip disabled, No cwip account set
|
||||
pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
|
||||
asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
asset_doc.available_for_use_date = nowdate()
|
||||
asset_doc.calculate_depreciation = 0
|
||||
asset_doc.submit()
|
||||
gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
|
||||
self.assertFalse(gle)
|
||||
|
||||
# case 1 -- PR with cwip disabled, Asset with cwip enabled
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
|
||||
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
asset_doc.available_for_use_date = nowdate()
|
||||
asset_doc.calculate_depreciation = 0
|
||||
asset_doc.submit()
|
||||
gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
|
||||
self.assertFalse(gle)
|
||||
|
||||
# case 2 -- PR with cwip enabled, Asset with cwip disabled
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location")
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
asset_doc.available_for_use_date = nowdate()
|
||||
asset_doc.calculate_depreciation = 0
|
||||
asset_doc.submit()
|
||||
gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
|
||||
self.assertTrue(gle)
|
||||
|
||||
# case 3 -- PI with cwip disabled, Asset with cwip enabled
|
||||
pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 1)
|
||||
asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
asset_doc.available_for_use_date = nowdate()
|
||||
asset_doc.calculate_depreciation = 0
|
||||
asset_doc.submit()
|
||||
gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
|
||||
self.assertFalse(gle)
|
||||
|
||||
# case 4 -- PI with cwip enabled, Asset with cwip disabled
|
||||
pi = make_purchase_invoice(item_code="Macbook Pro", qty=1, rate=200000.0, location="Test Location", update_stock=1)
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", 0)
|
||||
asset = frappe.db.get_value('Asset', {'purchase_invoice': pi.name, 'docstatus': 0}, 'name')
|
||||
asset_doc = frappe.get_doc('Asset', asset)
|
||||
asset_doc.available_for_use_date = nowdate()
|
||||
asset_doc.calculate_depreciation = 0
|
||||
asset_doc.submit()
|
||||
gle = frappe.db.sql("""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""", asset_doc.name)
|
||||
self.assertTrue(gle)
|
||||
|
||||
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", cwip)
|
||||
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
|
||||
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
|
||||
|
||||
def create_asset_data():
|
||||
if not frappe.db.exists("Asset Category", "Computers"):
|
||||
create_asset_category()
|
||||
|
@ -5,7 +5,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint
|
||||
from frappe.utils import cint, get_link_to_form
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AssetCategory(Document):
|
||||
@ -13,6 +13,7 @@ class AssetCategory(Document):
|
||||
self.validate_finance_books()
|
||||
self.validate_account_types()
|
||||
self.validate_account_currency()
|
||||
self.valide_cwip_account()
|
||||
|
||||
def validate_finance_books(self):
|
||||
for d in self.finance_books:
|
||||
@ -59,6 +60,21 @@ class AssetCategory(Document):
|
||||
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
|
||||
title=_("Invalid Account"))
|
||||
|
||||
def valide_cwip_account(self):
|
||||
if self.enable_cwip_accounting:
|
||||
missing_cwip_accounts_for_company = []
|
||||
for d in self.accounts:
|
||||
if (not d.capital_work_in_progress_account and
|
||||
not frappe.db.get_value("Company", d.company_name, "capital_work_in_progress_account")):
|
||||
missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name))
|
||||
|
||||
if missing_cwip_accounts_for_company:
|
||||
msg = _("""To enable Capital Work in Progress Accounting, """)
|
||||
msg += _("""you must select Capital Work in Progress Account in accounts table""")
|
||||
msg += "<br><br>"
|
||||
msg += _("You can also set default CWIP account in Company {}").format(", ".join(missing_cwip_accounts_for_company))
|
||||
frappe.throw(msg, title=_("Missing Account"))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||
|
@ -27,3 +27,21 @@ class TestAssetCategory(unittest.TestCase):
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
def test_cwip_accounting(self):
|
||||
company_cwip_acc = frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account")
|
||||
frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "")
|
||||
|
||||
asset_category = frappe.new_doc("Asset Category")
|
||||
asset_category.asset_category_name = "Computers"
|
||||
asset_category.enable_cwip_accounting = 1
|
||||
|
||||
asset_category.total_number_of_depreciations = 3
|
||||
asset_category.frequency_of_depreciation = 3
|
||||
asset_category.append("accounts", {
|
||||
"company_name": "_Test Company",
|
||||
"fixed_asset_account": "_Test Fixed Asset - _TC",
|
||||
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
|
||||
"depreciation_expense_account": "_Test Depreciations - _TC"
|
||||
})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, asset_category.insert)
|
@ -33,7 +33,7 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Other Reports",
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Quoted Item Comparison\",\n \"name\": \"Quoted Item Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
|
||||
"links": "[\n {\n \"is_query_report\": true,\n \"label\": \"Items To Be Requested\",\n \"name\": \"Items To Be Requested\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Item-wise Purchase History\",\n \"name\": \"Item-wise Purchase History\",\n \"onboard\": 1,\n \"reference_doctype\": \"Item\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Receipt Trends\",\n \"name\": \"Purchase Receipt Trends\",\n \"reference_doctype\": \"Purchase Receipt\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Purchase Invoice Trends\",\n \"name\": \"Purchase Invoice Trends\",\n \"reference_doctype\": \"Purchase Invoice\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Raw Materials To Be Transferred\",\n \"name\": \"Subcontracted Raw Materials To Be Transferred\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Subcontracted Item To Be Received\",\n \"name\": \"Subcontracted Item To Be Received\",\n \"reference_doctype\": \"Purchase Order\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Quotation Comparison\",\n \"name\": \"Supplier Quotation Comparison\",\n \"onboard\": 1,\n \"reference_doctype\": \"Supplier Quotation\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Material Requests for which Supplier Quotations are not created\",\n \"name\": \"Material Requests for which Supplier Quotations are not created\",\n \"reference_doctype\": \"Material Request\",\n \"type\": \"report\"\n },\n {\n \"is_query_report\": true,\n \"label\": \"Supplier Addresses And Contacts\",\n \"name\": \"Address And Contacts\",\n \"reference_doctype\": \"Address\",\n \"route_options\": {\n \"party_type\": \"Supplier\"\n },\n \"type\": \"report\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
@ -60,7 +60,7 @@
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Buying",
|
||||
"modified": "2020-06-29 19:30:24.983050",
|
||||
"modified": "2020-09-30 14:40:55.638458",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying",
|
||||
|
@ -651,12 +651,12 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
make_subcontracted_item(item_code)
|
||||
|
||||
po = create_purchase_order(item_code=item_code, qty=1,
|
||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
|
||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=1)
|
||||
|
||||
name = frappe.db.get_value('BOM', {'item': item_code}, 'name')
|
||||
bom = frappe.get_doc('BOM', name)
|
||||
|
||||
exploded_items = sorted([d.item_code for d in bom.exploded_items])
|
||||
exploded_items = sorted([d.item_code for d in bom.exploded_items if not d.get('sourced_by_supplier')])
|
||||
supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
|
||||
self.assertEquals(exploded_items, supplied_items)
|
||||
|
||||
@ -664,7 +664,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC", include_exploded_items=0)
|
||||
|
||||
supplied_items1 = sorted([d.rm_item_code for d in po1.supplied_items])
|
||||
bom_items = sorted([d.item_code for d in bom.items])
|
||||
bom_items = sorted([d.item_code for d in bom.items if not d.get('sourced_by_supplier')])
|
||||
|
||||
self.assertEquals(supplied_items1, bom_items)
|
||||
|
||||
|
@ -179,7 +179,7 @@ frappe.ui.form.on("Request for Quotation",{
|
||||
dialog.hide();
|
||||
return frappe.call({
|
||||
type: "GET",
|
||||
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation",
|
||||
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation_from_rfq",
|
||||
args: {
|
||||
"source_name": doc.name,
|
||||
"for_supplier": args.supplier
|
||||
|
@ -214,10 +214,10 @@ def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
|
||||
and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
|
||||
limit %(start)s, %(page_len)s""", {"start": start, "page_len":page_len, "txt": "%%%s%%" % txt, "name": filters.get('supplier')})
|
||||
|
||||
# This method is used to make supplier quotation from material request form.
|
||||
@frappe.whitelist()
|
||||
def make_supplier_quotation(source_name, for_supplier, target_doc=None):
|
||||
def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
|
||||
def postprocess(source, target_doc):
|
||||
if for_supplier:
|
||||
target_doc.supplier = for_supplier
|
||||
args = get_party_details(for_supplier, party_type="Supplier", ignore_permissions=True)
|
||||
target_doc.currency = args.currency or get_party_account_currency('Supplier', for_supplier, source.company)
|
||||
@ -354,3 +354,32 @@ def get_supplier_tag():
|
||||
frappe.cache().hset("Supplier", "Tags", tags)
|
||||
|
||||
return frappe.cache().hget("Supplier", "Tags")
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_rfq_containing_supplier(doctype, txt, searchfield, start, page_len, filters):
|
||||
conditions = ""
|
||||
if txt:
|
||||
conditions += "and rfq.name like '%%"+txt+"%%' "
|
||||
|
||||
if filters.get("transaction_date"):
|
||||
conditions += "and rfq.transaction_date = '{0}'".format(filters.get("transaction_date"))
|
||||
|
||||
rfq_data = frappe.db.sql("""
|
||||
select
|
||||
distinct rfq.name, rfq.transaction_date,
|
||||
rfq.company
|
||||
from
|
||||
`tabRequest for Quotation` rfq, `tabRequest for Quotation Supplier` rfq_supplier
|
||||
where
|
||||
rfq.name = rfq_supplier.parent
|
||||
and rfq_supplier.supplier = '{0}'
|
||||
and rfq.docstatus = 1
|
||||
and rfq.company = '{1}'
|
||||
{2}
|
||||
order by rfq.transaction_date ASC
|
||||
limit %(page_len)s offset %(start)s """ \
|
||||
.format(filters.get("supplier"), filters.get("company"), conditions),
|
||||
{"page_len": page_len, "start": start}, as_dict=1)
|
||||
|
||||
return rfq_data
|
@ -9,7 +9,7 @@ import frappe
|
||||
from frappe.utils import nowdate
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import make_supplier_quotation_from_rfq
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import create_supplier_quotation
|
||||
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
||||
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
||||
@ -22,7 +22,7 @@ class TestRequestforQuotation(unittest.TestCase):
|
||||
self.assertEqual(rfq.get('suppliers')[1].quote_status, 'Pending')
|
||||
|
||||
# Submit the first supplier quotation
|
||||
sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
|
||||
sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
|
||||
sq.submit()
|
||||
|
||||
# No Quote first supplier quotation
|
||||
@ -37,10 +37,10 @@ class TestRequestforQuotation(unittest.TestCase):
|
||||
def test_make_supplier_quotation(self):
|
||||
rfq = make_request_for_quotation()
|
||||
|
||||
sq = make_supplier_quotation(rfq.name, rfq.get('suppliers')[0].supplier)
|
||||
sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[0].supplier)
|
||||
sq.submit()
|
||||
|
||||
sq1 = make_supplier_quotation(rfq.name, rfq.get('suppliers')[1].supplier)
|
||||
sq1 = make_supplier_quotation_from_rfq(rfq.name, for_supplier=rfq.get('suppliers')[1].supplier)
|
||||
sq1.submit()
|
||||
|
||||
self.assertEqual(sq.supplier, rfq.get('suppliers')[0].supplier)
|
||||
@ -62,7 +62,7 @@ class TestRequestforQuotation(unittest.TestCase):
|
||||
|
||||
rfq = make_request_for_quotation(supplier_data=supplier_wt_appos)
|
||||
|
||||
sq = make_supplier_quotation(rfq.name, supplier_wt_appos[0].get("supplier"))
|
||||
sq = make_supplier_quotation_from_rfq(rfq.name, for_supplier=supplier_wt_appos[0].get("supplier"))
|
||||
sq.submit()
|
||||
|
||||
frappe.form_dict = frappe.local("form_dict")
|
||||
|
@ -8,8 +8,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
setup: function() {
|
||||
this.frm.custom_make_buttons = {
|
||||
'Purchase Order': 'Purchase Order',
|
||||
'Quotation': 'Quotation',
|
||||
'Subscription': 'Subscription'
|
||||
'Quotation': 'Quotation'
|
||||
}
|
||||
|
||||
this._super();
|
||||
@ -28,12 +27,6 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
cur_frm.add_custom_button(__("Quotation"), this.make_quotation,
|
||||
__('Create'));
|
||||
|
||||
if(!this.frm.doc.auto_repeat) {
|
||||
cur_frm.add_custom_button(__('Subscription'), function() {
|
||||
erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name)
|
||||
}, __('Create'))
|
||||
}
|
||||
}
|
||||
else if (this.frm.doc.docstatus===0) {
|
||||
|
||||
@ -54,6 +47,27 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
|
||||
this.frm.add_custom_button(__("Request for Quotation"),
|
||||
function() {
|
||||
if (!me.frm.doc.supplier) {
|
||||
frappe.throw({message:__("Please select a Supplier"), title:__("Mandatory")})
|
||||
}
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.make_supplier_quotation_from_rfq",
|
||||
source_doctype: "Request for Quotation",
|
||||
target: me.frm,
|
||||
setters: {
|
||||
company: me.frm.doc.company,
|
||||
transaction_date: null
|
||||
},
|
||||
get_query_filters: {
|
||||
supplier: me.frm.doc.supplier
|
||||
},
|
||||
get_query_method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_rfq_containing_supplier"
|
||||
|
||||
})
|
||||
}, __("Get items from"));
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -159,6 +159,7 @@
|
||||
"default": "Today",
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Date",
|
||||
"oldfieldname": "transaction_date",
|
||||
"oldfieldtype": "Date",
|
||||
@ -798,6 +799,7 @@
|
||||
{
|
||||
"fieldname": "valid_till",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Valid Till"
|
||||
}
|
||||
],
|
||||
@ -805,7 +807,7 @@
|
||||
"idx": 29,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-18 05:10:45.556792",
|
||||
"modified": "2020-10-01 20:56:17.932007",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
@ -12,6 +12,8 @@
|
||||
"item_name",
|
||||
"column_break_3",
|
||||
"lead_time_days",
|
||||
"expected_delivery_date",
|
||||
"is_free_item",
|
||||
"section_break_5",
|
||||
"description",
|
||||
"item_group",
|
||||
@ -19,20 +21,18 @@
|
||||
"col_break1",
|
||||
"image",
|
||||
"image_view",
|
||||
"manufacture_details",
|
||||
"manufacturer",
|
||||
"column_break_15",
|
||||
"manufacturer_part_no",
|
||||
"quantity_and_rate",
|
||||
"qty",
|
||||
"stock_uom",
|
||||
"price_list_rate",
|
||||
"discount_percentage",
|
||||
"discount_amount",
|
||||
"col_break2",
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"stock_qty",
|
||||
"sec_break_price_list",
|
||||
"price_list_rate",
|
||||
"discount_percentage",
|
||||
"discount_amount",
|
||||
"col_break_price_list",
|
||||
"base_price_list_rate",
|
||||
"sec_break1",
|
||||
"rate",
|
||||
@ -42,7 +42,6 @@
|
||||
"base_rate",
|
||||
"base_amount",
|
||||
"pricing_rules",
|
||||
"is_free_item",
|
||||
"section_break_24",
|
||||
"net_rate",
|
||||
"net_amount",
|
||||
@ -56,7 +55,6 @@
|
||||
"weight_uom",
|
||||
"warehouse_and_reference",
|
||||
"warehouse",
|
||||
"project",
|
||||
"prevdoc_doctype",
|
||||
"material_request",
|
||||
"sales_order",
|
||||
@ -65,13 +63,19 @@
|
||||
"material_request_item",
|
||||
"request_for_quotation_item",
|
||||
"item_tax_rate",
|
||||
"manufacture_details",
|
||||
"manufacturer",
|
||||
"column_break_15",
|
||||
"manufacturer_part_no",
|
||||
"ad_sec_break",
|
||||
"project",
|
||||
"section_break_44",
|
||||
"page_break"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"bold": 1,
|
||||
"columns": 4,
|
||||
"columns": 2,
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
@ -107,7 +111,7 @@
|
||||
{
|
||||
"fieldname": "lead_time_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Lead Time in days"
|
||||
"label": "Supplier Lead Time (days)"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@ -162,7 +166,6 @@
|
||||
{
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Stock UOM",
|
||||
"options": "UOM",
|
||||
"print_hide": 1,
|
||||
@ -196,6 +199,7 @@
|
||||
{
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "UOM",
|
||||
"options": "UOM",
|
||||
"print_hide": 1,
|
||||
@ -289,14 +293,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_free_item",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Free Item",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_24",
|
||||
"fieldtype": "Section Break"
|
||||
@ -528,12 +524,43 @@
|
||||
{
|
||||
"fieldname": "column_break_15",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "sec_break_price_list",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break_price_list",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "ad_sec_break",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounting Dimensions"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "is_free_item",
|
||||
"fieldname": "is_free_item",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Free Item",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"bold": 1,
|
||||
"fieldname": "expected_delivery_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Expected Delivery Date"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-07 18:35:51.175947",
|
||||
"modified": "2020-10-01 16:34:39.703033",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation Item",
|
||||
|
@ -1,32 +0,0 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2016-07-21 08:31:05.890362",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 2,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:04:58.784351",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Quoted Item Comparison",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Supplier Quotation",
|
||||
"report_name": "Quoted Item Comparison",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Quoted Item Comparison"] = {
|
||||
frappe.query_reports["Supplier Quotation Comparison"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldtype: "Link",
|
||||
@ -78,6 +78,13 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
return { filters: { "docstatus": ["<", 2] } }
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"group_by",
|
||||
"label": __("Group by"),
|
||||
"fieldtype": "Select",
|
||||
"options": [__("Group by Supplier"), __("Group by Item")],
|
||||
"default": __("Group by Supplier")
|
||||
},
|
||||
{
|
||||
fieldtype: "Check",
|
||||
label: __("Include Expired"),
|
||||
@ -98,6 +105,9 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
}
|
||||
}
|
||||
|
||||
if(column.fieldname === "price_per_unit" && data.price_per_unit && data.min && data.min === 1){
|
||||
value = `<div style="color:green">${value}</div>`;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
@ -0,0 +1,32 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2016-07-21 08:31:05.890362",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 2,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:04:58.784351",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation Comparison",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Supplier Quotation",
|
||||
"report_name": "Supplier Quotation Comparison",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Manufacturing Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase Manager"
|
||||
},
|
||||
{
|
||||
"role": "Purchase User"
|
||||
},
|
||||
{
|
||||
"role": "Stock User"
|
||||
}
|
||||
]
|
||||
}
|
@ -12,9 +12,9 @@ def execute(filters=None):
|
||||
if not filters:
|
||||
return [], []
|
||||
|
||||
columns = get_columns(filters)
|
||||
conditions = get_conditions(filters)
|
||||
supplier_quotation_data = get_data(filters, conditions)
|
||||
columns = get_columns()
|
||||
|
||||
data, chart_data = prepare_data(supplier_quotation_data, filters)
|
||||
message = get_message()
|
||||
@ -41,9 +41,13 @@ def get_conditions(filters):
|
||||
return conditions
|
||||
|
||||
def get_data(filters, conditions):
|
||||
supplier_quotation_data = frappe.db.sql("""SELECT
|
||||
sqi.parent, sqi.item_code, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
|
||||
sqi.lead_time_days, sq.supplier, sq.valid_till
|
||||
supplier_quotation_data = frappe.db.sql("""
|
||||
SELECT
|
||||
sqi.parent, sqi.item_code,
|
||||
sqi.qty, sqi.stock_qty, sqi.amount,
|
||||
sqi.uom, sqi.stock_uom,
|
||||
sqi.request_for_quotation,
|
||||
sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till
|
||||
FROM
|
||||
`tabSupplier Quotation Item` sqi,
|
||||
`tabSupplier Quotation` sq
|
||||
@ -58,16 +62,18 @@ def get_data(filters, conditions):
|
||||
return supplier_quotation_data
|
||||
|
||||
def prepare_data(supplier_quotation_data, filters):
|
||||
out, suppliers, qty_list, chart_data = [], [], [], []
|
||||
supplier_wise_map = defaultdict(list)
|
||||
out, groups, qty_list, suppliers, chart_data = [], [], [], [], []
|
||||
group_wise_map = defaultdict(list)
|
||||
supplier_qty_price_map = {}
|
||||
|
||||
group_by_field = "supplier_name" if filters.get("group_by") == "Group by Supplier" else "item_code"
|
||||
company_currency = frappe.db.get_default("currency")
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
|
||||
for data in supplier_quotation_data:
|
||||
supplier = data.get("supplier")
|
||||
supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency")
|
||||
group = data.get(group_by_field) # get item or supplier value for this row
|
||||
|
||||
supplier_currency = frappe.db.get_value("Supplier", data.get("supplier_name"), "default_currency")
|
||||
|
||||
if supplier_currency:
|
||||
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
|
||||
@ -75,38 +81,55 @@ def prepare_data(supplier_quotation_data, filters):
|
||||
exchange_rate = 1
|
||||
|
||||
row = {
|
||||
"item_code": data.get('item_code'),
|
||||
"item_code": "" if group_by_field=="item_code" else data.get("item_code"), # leave blank if group by field
|
||||
"supplier_name": "" if group_by_field=="supplier_name" else data.get("supplier_name"),
|
||||
"quotation": data.get("parent"),
|
||||
"qty": data.get("qty"),
|
||||
"price": flt(data.get("rate") * exchange_rate, float_precision),
|
||||
"price": flt(data.get("amount") * exchange_rate, float_precision),
|
||||
"uom": data.get("uom"),
|
||||
"stock_uom": data.get('stock_uom'),
|
||||
"request_for_quotation": data.get("request_for_quotation"),
|
||||
"valid_till": data.get('valid_till'),
|
||||
"lead_time_days": data.get('lead_time_days')
|
||||
}
|
||||
row["price_per_unit"] = flt(row["price"]) / (flt(data.get("stock_qty")) or 1)
|
||||
|
||||
# map for report view of form {'supplier1':[{},{},...]}
|
||||
supplier_wise_map[supplier].append(row)
|
||||
# map for report view of form {'supplier1'/'item1':[{},{},...]}
|
||||
group_wise_map[group].append(row)
|
||||
|
||||
# map for chart preparation of the form {'supplier1': {'qty': 'price'}}
|
||||
supplier = data.get("supplier_name")
|
||||
if filters.get("item_code"):
|
||||
if not supplier in supplier_qty_price_map:
|
||||
supplier_qty_price_map[supplier] = {}
|
||||
supplier_qty_price_map[supplier][row["qty"]] = row["price"]
|
||||
|
||||
groups.append(group)
|
||||
suppliers.append(supplier)
|
||||
qty_list.append(data.get("qty"))
|
||||
|
||||
groups = list(set(groups))
|
||||
suppliers = list(set(suppliers))
|
||||
qty_list = list(set(qty_list))
|
||||
|
||||
highlight_min_price = group_by_field == "item_code" or filters.get("item_code")
|
||||
|
||||
# final data format for report view
|
||||
for supplier in suppliers:
|
||||
supplier_wise_map[supplier][0].update({"supplier_name": supplier})
|
||||
for entry in supplier_wise_map[supplier]:
|
||||
for group in groups:
|
||||
group_entries = group_wise_map[group] # all entries pertaining to item/supplier
|
||||
group_entries[0].update({group_by_field : group}) # Add item/supplier name in first group row
|
||||
|
||||
if highlight_min_price:
|
||||
prices = [group_entry["price_per_unit"] for group_entry in group_entries]
|
||||
min_price = min(prices)
|
||||
|
||||
for entry in group_entries:
|
||||
if highlight_min_price and entry["price_per_unit"] == min_price:
|
||||
entry["min"] = 1
|
||||
out.append(entry)
|
||||
|
||||
if filters.get("item_code"):
|
||||
# render chart only for one item comparison
|
||||
chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
|
||||
|
||||
return out, chart_data
|
||||
@ -145,8 +168,9 @@ def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
|
||||
|
||||
return chart_data
|
||||
|
||||
def get_columns():
|
||||
columns = [{
|
||||
def get_columns(filters):
|
||||
group_by_columns = [
|
||||
{
|
||||
"fieldname": "supplier_name",
|
||||
"label": _("Supplier"),
|
||||
"fieldtype": "Link",
|
||||
@ -158,8 +182,10 @@ def get_columns():
|
||||
"label": _("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"width": 200
|
||||
},
|
||||
"width": 150
|
||||
}]
|
||||
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "uom",
|
||||
"label": _("UOM"),
|
||||
@ -180,6 +206,20 @@ def get_columns():
|
||||
"options": "Company:company:default_currency",
|
||||
"width": 110
|
||||
},
|
||||
{
|
||||
"fieldname": "stock_uom",
|
||||
"label": _("Stock UOM"),
|
||||
"fieldtype": "Link",
|
||||
"options": "UOM",
|
||||
"width": 90
|
||||
},
|
||||
{
|
||||
"fieldname": "price_per_unit",
|
||||
"label": _("Price per Unit (Stock UOM)"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"width": 120
|
||||
},
|
||||
{
|
||||
"fieldname": "quotation",
|
||||
"label": _("Supplier Quotation"),
|
||||
@ -205,9 +245,12 @@ def get_columns():
|
||||
"fieldtype": "Link",
|
||||
"options": "Request for Quotation",
|
||||
"width": 150
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
||||
if filters.get("group_by") == "Group by Item":
|
||||
group_by_columns.reverse()
|
||||
|
||||
columns[0:0] = group_by_columns # add positioned group by columns to the report
|
||||
return columns
|
||||
|
||||
def get_message():
|
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import cint, flt, cstr, comma_or
|
||||
from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
|
||||
from frappe import _, throw
|
||||
from erpnext.stock.get_item_details import get_bin_details
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
@ -173,12 +173,16 @@ class SellingController(StockController):
|
||||
|
||||
def validate_selling_price(self):
|
||||
def throw_message(idx, item_name, rate, ref_rate_field):
|
||||
frappe.throw(_("""Row #{}: Selling rate for item {} is lower than its {}. Selling rate should be atleast {}""")
|
||||
.format(idx, item_name, ref_rate_field, rate))
|
||||
bold_net_rate = frappe.bold("net rate")
|
||||
msg = (_("""Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {}""")
|
||||
.format(idx, frappe.bold(item_name), frappe.bold(ref_rate_field), bold_net_rate, frappe.bold(rate)))
|
||||
msg += "<br><br>"
|
||||
msg += (_("""You can alternatively disable selling price validation in {} to bypass this validation.""")
|
||||
.format(get_link_to_form("Selling Settings", "Selling Settings")))
|
||||
frappe.throw(msg, title=_("Invalid Selling Price"))
|
||||
|
||||
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
|
||||
return
|
||||
|
||||
if hasattr(self, "is_return") and self.is_return:
|
||||
return
|
||||
|
||||
@ -187,8 +191,8 @@ class SellingController(StockController):
|
||||
continue
|
||||
|
||||
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
||||
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
|
||||
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||
last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
|
||||
if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||
throw_message(it.idx, frappe.bold(it.item_name), last_purchase_rate_in_sales_uom, "last purchase rate")
|
||||
|
||||
last_valuation_rate = frappe.db.sql("""
|
||||
@ -197,8 +201,8 @@ class SellingController(StockController):
|
||||
ORDER BY posting_date DESC, posting_time DESC, creation DESC LIMIT 1
|
||||
""", (it.item_code, it.warehouse))
|
||||
if last_valuation_rate:
|
||||
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
|
||||
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom) \
|
||||
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] * (it.conversion_factor or 1)
|
||||
if is_stock_item and flt(it.base_net_rate) < flt(last_valuation_rate_in_sales_uom) \
|
||||
and not self.get('is_internal_customer'):
|
||||
throw_message(it.idx, frappe.bold(it.item_name), last_valuation_rate_in_sales_uom, "valuation rate")
|
||||
|
||||
|
@ -241,6 +241,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"description": "Home, Work, etc.",
|
||||
"fieldname": "address_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Address Title"
|
||||
@ -249,7 +250,8 @@
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "address_line1",
|
||||
"fieldtype": "Data",
|
||||
"label": "Address Line 1"
|
||||
"label": "Address Line 1",
|
||||
"mandatory_depends_on": "eval: doc.address_title && doc.address_type"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
@ -261,7 +263,8 @@
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
"fieldname": "city",
|
||||
"fieldtype": "Data",
|
||||
"label": "City/Town"
|
||||
"label": "City/Town",
|
||||
"mandatory_depends_on": "eval: doc.address_title && doc.address_type"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.__islocal",
|
||||
@ -280,6 +283,7 @@
|
||||
"fieldname": "country",
|
||||
"fieldtype": "Link",
|
||||
"label": "Country",
|
||||
"mandatory_depends_on": "eval: doc.address_title && doc.address_type",
|
||||
"options": "Country"
|
||||
},
|
||||
{
|
||||
@ -449,7 +453,7 @@
|
||||
"idx": 5,
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"modified": "2020-06-18 14:39:41.835416",
|
||||
"modified": "2020-10-13 15:24:00.094811",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Lead",
|
||||
|
@ -22,6 +22,7 @@ class Lead(SellingController):
|
||||
load_address_and_contact(self)
|
||||
|
||||
def before_insert(self):
|
||||
if self.address_title and self.address_type:
|
||||
self.address_doc = self.create_address()
|
||||
self.contact_doc = self.create_contact()
|
||||
|
||||
@ -133,15 +134,6 @@ class Lead(SellingController):
|
||||
# skipping country since the system auto-sets it from system defaults
|
||||
address = frappe.new_doc("Address")
|
||||
|
||||
mandatory_fields = [ df.fieldname for df in address.meta.fields if df.reqd ]
|
||||
|
||||
if not all([self.get(field) for field in mandatory_fields]):
|
||||
frappe.msgprint(_('Missing mandatory fields in address. \
|
||||
{0} to create address' ).format("<a href='desk#Form/Address/New Address 1' \
|
||||
> Click here </a>"),
|
||||
alert=True, indicator='yellow')
|
||||
return
|
||||
|
||||
address.update({addr_field: self.get(addr_field) for addr_field in address_fields})
|
||||
address.update({info_field: self.get(info_field) for info_field in info_fields})
|
||||
address.insert()
|
||||
@ -190,7 +182,7 @@ class Lead(SellingController):
|
||||
|
||||
def update_links(self):
|
||||
# update address links
|
||||
if self.address_doc:
|
||||
if hasattr(self, 'address_doc'):
|
||||
self.address_doc.append("links", {
|
||||
"link_doctype": "Lead",
|
||||
"link_name": self.name,
|
||||
|
@ -11,7 +11,7 @@ from erpnext.accounts.party import get_party_account_currency
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
from erpnext.stock.doctype.material_request.material_request import make_request_for_quotation
|
||||
from erpnext.buying.doctype.request_for_quotation.request_for_quotation import \
|
||||
make_supplier_quotation as make_quotation_from_rfq
|
||||
make_supplier_quotation_from_rfq
|
||||
|
||||
def work():
|
||||
frappe.set_user(frappe.db.get_global('demo_purchase_user'))
|
||||
@ -44,7 +44,7 @@ def work():
|
||||
rfq = frappe.get_doc('Request for Quotation', rfq.name)
|
||||
|
||||
for supplier in rfq.suppliers:
|
||||
supplier_quotation = make_quotation_from_rfq(rfq.name, supplier.supplier)
|
||||
supplier_quotation = make_supplier_quotation_from_rfq(rfq.name, for_supplier=supplier.supplier)
|
||||
supplier_quotation.save()
|
||||
supplier_quotation.submit()
|
||||
|
||||
|
@ -397,6 +397,9 @@ regional_overrides = {
|
||||
'Italy': {
|
||||
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
|
||||
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
|
||||
},
|
||||
'Germany': {
|
||||
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional',
|
||||
}
|
||||
}
|
||||
user_privacy_documents = [
|
||||
|
@ -109,7 +109,6 @@
|
||||
"encashment_date",
|
||||
"exit_interview_details",
|
||||
"held_on",
|
||||
"reason_for_resignation",
|
||||
"new_workplace",
|
||||
"feedback",
|
||||
"lft",
|
||||
@ -682,7 +681,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "reason_for_leaving",
|
||||
"fieldtype": "Data",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Reason for Leaving",
|
||||
"oldfieldname": "reason_for_leaving",
|
||||
"oldfieldtype": "Data"
|
||||
@ -696,6 +695,7 @@
|
||||
"options": "\nYes\nNo"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.leave_encashed ==\"Yes\"",
|
||||
"fieldname": "encashment_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Encashment Date",
|
||||
@ -705,7 +705,6 @@
|
||||
{
|
||||
"fieldname": "exit_interview_details",
|
||||
"fieldtype": "Column Break",
|
||||
"label": "Exit Interview Details",
|
||||
"oldfieldname": "col_brk6",
|
||||
"oldfieldtype": "Column Break",
|
||||
"width": "50%"
|
||||
@ -713,18 +712,10 @@
|
||||
{
|
||||
"fieldname": "held_on",
|
||||
"fieldtype": "Date",
|
||||
"label": "Held On",
|
||||
"label": "Exit Interview Held On",
|
||||
"oldfieldname": "held_on",
|
||||
"oldfieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "reason_for_resignation",
|
||||
"fieldtype": "Select",
|
||||
"label": "Reason for Resignation",
|
||||
"oldfieldname": "reason_for_resignation",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nBetter Prospects\nHealth Concerns"
|
||||
},
|
||||
{
|
||||
"fieldname": "new_workplace",
|
||||
"fieldtype": "Data",
|
||||
@ -809,37 +800,29 @@
|
||||
"fieldname": "expense_approver",
|
||||
"fieldtype": "Link",
|
||||
"label": "Expense Approver",
|
||||
"options": "User",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"fieldname": "approvers_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Approvers",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Approvers"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_45",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "shift_request_approver",
|
||||
"fieldtype": "Link",
|
||||
"label": "Shift Request Approver",
|
||||
"options": "User",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "User"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-user",
|
||||
"idx": 24,
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"modified": "2020-07-28 01:36:04.109189",
|
||||
"modified": "2020-10-06 15:58:23.805489",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Employee",
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
@ -32,7 +33,7 @@ class HolidayList(Document):
|
||||
|
||||
|
||||
def validate_days(self):
|
||||
if self.from_date > self.to_date:
|
||||
if getdate(self.from_date) > getdate(self.to_date):
|
||||
throw(_("To Date cannot be before From Date"))
|
||||
|
||||
for day in self.get("holidays"):
|
||||
|
@ -207,7 +207,6 @@ class TestBOM(unittest.TestCase):
|
||||
supplied_items = sorted([d.rm_item_code for d in po.supplied_items])
|
||||
self.assertEquals(bom_items, supplied_items)
|
||||
|
||||
|
||||
def get_default_bom(item_code="_Test FG Item 2"):
|
||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||
|
||||
|
@ -381,7 +381,6 @@ class ProductionPlan(Document):
|
||||
"transaction_date": nowdate(),
|
||||
"status": "Draft",
|
||||
"company": self.company,
|
||||
"requested_by": frappe.session.user,
|
||||
'material_request_type': material_request_type,
|
||||
'customer': item_doc.customer or ''
|
||||
})
|
||||
|
@ -434,7 +434,7 @@ class WorkOrder(Document):
|
||||
elif flt(d.completed_qty) <= max_allowed_qty_for_wo:
|
||||
d.status = "Completed"
|
||||
else:
|
||||
frappe.throw(_("Completed Qty can not be greater than 'Qty to Manufacture'"))
|
||||
frappe.throw(_("Completed Qty cannot be greater than 'Qty to Manufacture'"))
|
||||
|
||||
def set_actual_dates(self):
|
||||
if self.get("operations"):
|
||||
|
@ -729,3 +729,4 @@ erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports
|
||||
erpnext.patches.v13_0.rename_issue_doctype_fields
|
||||
erpnext.patches.v13_0.change_default_pos_print_format
|
||||
erpnext.patches.v13_0.set_youtube_video_id
|
||||
erpnext.patches.v13_0.print_uom_after_quantity_patch
|
10
erpnext/patches/v13_0/print_uom_after_quantity_patch.py
Normal file
10
erpnext/patches/v13_0/print_uom_after_quantity_patch.py
Normal file
@ -0,0 +1,10 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from erpnext.setup.install import create_print_uom_after_qty_custom_field
|
||||
|
||||
def execute():
|
||||
create_print_uom_after_qty_custom_field()
|
@ -217,7 +217,7 @@
|
||||
"fieldname": "help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Help",
|
||||
"options": "<h3>Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condtion. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
||||
"options": "<h3>Help</h3>\n\n<p>Notes:</p>\n\n<ol>\n<li>Use field <code>base</code> for using base salary of the Employee</li>\n<li>Use Salary Component abbreviations in conditions and formulas. <code>BS = Basic Salary</code></li>\n<li>Use field name for employee details in conditions and formulas. <code>Employment Type = employment_type</code><code>Branch = branch</code></li>\n<li>Use field name from Salary Slip in conditions and formulas. <code>Payment Days = payment_days</code><code>Leave without pay = leave_without_pay</code></li>\n<li>Direct Amount can also be entered based on Condition. See example 3</li></ol>\n\n<h4>Examples</h4>\n<ol>\n<li>Calculating Basic Salary based on <code>base</code>\n<pre><code>Condition: base < 10000</code></pre>\n<pre><code>Formula: base * .2</code></pre></li>\n<li>Calculating HRA based on Basic Salary<code>BS</code> \n<pre><code>Condition: BS > 2000</code></pre>\n<pre><code>Formula: BS * .1</code></pre></li>\n<li>Calculating TDS based on Employment Type<code>employment_type</code> \n<pre><code>Condition: employment_type==\"Intern\"</code></pre>\n<pre><code>Amount: 1000</code></pre></li>\n</ol>"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@ -238,14 +238,13 @@
|
||||
"depends_on": "eval:doc.type == \"Deduction\"",
|
||||
"fieldname": "is_income_tax_component",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Income Tax Component",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Is Income Tax Component"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-flag",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-22 15:39:20.826565",
|
||||
"modified": "2020-10-07 20:38:33.795853",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Component",
|
||||
|
@ -117,7 +117,7 @@
|
||||
"depends_on": "eval:doc.is_flexible_benefit != 1",
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Condtion and formula"
|
||||
"label": "Condition and formula"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
@ -206,38 +206,28 @@
|
||||
"collapsible": 1,
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Component properties and references ",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Component properties and references "
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_19",
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_18",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_24",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-01 12:13:41.956495",
|
||||
"modified": "2020-10-07 20:39:41.619283",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Salary Detail",
|
||||
|
@ -347,8 +347,7 @@ class TestSalarySlip(unittest.TestCase):
|
||||
|
||||
# create additional salary of 150000
|
||||
frappe.db.sql("""delete from `tabSalary Slip` where employee=%s""", (employee))
|
||||
data["additional-1"] = create_additional_salary(employee, payroll_period, 50000)
|
||||
data["additional-2"] = create_additional_salary(employee, payroll_period, 100000)
|
||||
data["additional-1"] = create_additional_salary(employee, payroll_period, 150000)
|
||||
data["deducted_dates"] = create_salary_slips_for_payroll_period(employee,
|
||||
salary_structure.name, payroll_period)
|
||||
|
||||
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import time_diff_in_hours, flt
|
||||
from frappe.model.meta import get_field_precision
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
@ -136,6 +137,7 @@ def get_timesheet_details(filters, timesheet_list):
|
||||
return timesheet_details_map
|
||||
|
||||
def get_billable_and_total_duration(activity, start_time, end_time):
|
||||
precision = frappe.get_precision("Timesheet Detail", "hours")
|
||||
activity_duration = time_diff_in_hours(end_time, start_time)
|
||||
billing_duration = 0.0
|
||||
if activity.billable:
|
||||
@ -143,4 +145,4 @@ def get_billable_and_total_duration(activity, start_time, end_time):
|
||||
if activity_duration != activity.billing_hours:
|
||||
billing_duration = activity_duration * activity.billing_hours / activity.hours
|
||||
|
||||
return flt(activity_duration, 2), flt(billing_duration, 2)
|
||||
return flt(activity_duration, precision), flt(billing_duration, precision)
|
@ -309,7 +309,6 @@ erpnext.setup.fiscal_years = {
|
||||
"Hong Kong": ["04-01", "03-31"],
|
||||
"India": ["04-01", "03-31"],
|
||||
"Iran": ["06-23", "06-22"],
|
||||
"Italy": ["07-01", "06-30"],
|
||||
"Myanmar": ["04-01", "03-31"],
|
||||
"New Zealand": ["04-01", "03-31"],
|
||||
"Pakistan": ["07-01", "06-30"],
|
||||
|
@ -703,9 +703,13 @@ erpnext.utils.map_current_doc = function(opts) {
|
||||
}
|
||||
|
||||
frappe.form.link_formatters['Item'] = function(value, doc) {
|
||||
if(doc && doc.item_name && doc.item_name !== value) {
|
||||
return value? value + ': ' + doc.item_name: doc.item_name;
|
||||
if (doc && value && doc.item_name && doc.item_name !== value) {
|
||||
return value + ': ' + doc.item_name;
|
||||
} else if (!value && doc.doctype && doc.item_name) {
|
||||
// format blank value in child table
|
||||
return doc.item_name;
|
||||
} else {
|
||||
// if value is blank in report view or item code and name are the same, return as is
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,4 @@
|
||||
{% if address_line1 %}{{ address_line1 }}<br>{% endif -%}
|
||||
{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}
|
||||
{% if pincode %}L-{{ pincode }}{% endif -%}{% if city %} {{ city }}{% endif %}<br>
|
||||
{% if country %}{{ country | upper }}{% endif %}
|
57
erpnext/regional/germany/accounts_controller.py
Normal file
57
erpnext/regional/germany/accounts_controller.py
Normal file
@ -0,0 +1,57 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe import msgprint
|
||||
|
||||
|
||||
REQUIRED_FIELDS = {
|
||||
"Sales Invoice": [
|
||||
{
|
||||
"field_name": "company_address",
|
||||
"regulation": "§ 14 Abs. 4 Nr. 1 UStG"
|
||||
},
|
||||
{
|
||||
"field_name": "company_tax_id",
|
||||
"regulation": "§ 14 Abs. 4 Nr. 2 UStG"
|
||||
},
|
||||
{
|
||||
"field_name": "taxes",
|
||||
"regulation": "§ 14 Abs. 4 Nr. 8 UStG",
|
||||
"condition": "not exempt_from_sales_tax"
|
||||
},
|
||||
{
|
||||
"field_name": "customer_address",
|
||||
"regulation": "§ 14 Abs. 4 Nr. 1 UStG",
|
||||
"condition": "base_grand_total > 250"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
def validate_regional(doc):
|
||||
"""Check if required fields for this document are present."""
|
||||
required_fields = REQUIRED_FIELDS.get(doc.doctype)
|
||||
if not required_fields:
|
||||
return
|
||||
|
||||
meta = frappe.get_meta(doc.doctype)
|
||||
field_map = {field.fieldname: field.label for field in meta.fields}
|
||||
|
||||
for field in required_fields:
|
||||
condition = field.get("condition")
|
||||
if condition and not frappe.safe_eval(condition, doc.as_dict()):
|
||||
continue
|
||||
|
||||
field_name = field.get("field_name")
|
||||
regulation = field.get("regulation")
|
||||
if field_name and not doc.get(field_name):
|
||||
missing(field_map.get(field_name), regulation)
|
||||
|
||||
|
||||
def missing(field_label, regulation):
|
||||
"""Notify the user that a required field is missing."""
|
||||
context = 'Specific for Germany. Example: Remember to set Company Tax ID. It is required by § 14 Abs. 4 Nr. 2 UStG.'
|
||||
msgprint(_('Remember to set {field_label}. It is required by {regulation}.', context=context).format(
|
||||
field_label=frappe.bold(_(field_label)),
|
||||
regulation=regulation
|
||||
)
|
||||
)
|
@ -46,7 +46,7 @@ QUnit.test("test: quotation", function (assert) {
|
||||
assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
|
||||
assert.equal(cur_frm.doc.total, 1000, "New Total Calculated");
|
||||
|
||||
// Check Terms and Condtions
|
||||
// Check Terms and Conditions
|
||||
assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
|
||||
|
||||
assert.ok(cur_frm.doc.payment_terms_template, "Payment Terms Template is correct");
|
||||
|
@ -989,7 +989,6 @@ def make_raw_material_request(items, company, sales_order, project=None):
|
||||
doctype = 'Material Request',
|
||||
transaction_date = nowdate(),
|
||||
company = company,
|
||||
requested_by = frappe.session.user,
|
||||
material_request_type = 'Purchase'
|
||||
))
|
||||
for item in raw_materials:
|
||||
|
@ -442,7 +442,7 @@ def install_country_fixtures(company):
|
||||
module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(company_doc.country))
|
||||
frappe.get_attr(module_name)(company_doc, False)
|
||||
except Exception as e:
|
||||
frappe.log_error(str(e), frappe.get_traceback())
|
||||
frappe.log_error()
|
||||
frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(company_doc.country)))
|
||||
|
||||
|
||||
|
@ -60,14 +60,10 @@
|
||||
},
|
||||
|
||||
"Australia": {
|
||||
"Australia GST1": {
|
||||
"Australia GST": {
|
||||
"account_name": "GST 10%",
|
||||
"tax_rate": 10.00,
|
||||
"default": 1
|
||||
},
|
||||
"Australia GST 2%": {
|
||||
"account_name": "GST 2%",
|
||||
"tax_rate": 2
|
||||
}
|
||||
},
|
||||
|
||||
@ -648,9 +644,18 @@
|
||||
},
|
||||
|
||||
"Italy": {
|
||||
"Italy Tax": {
|
||||
"account_name": "VAT",
|
||||
"tax_rate": 22.00
|
||||
"Italy VAT 22%": {
|
||||
"account_name": "IVA 22%",
|
||||
"tax_rate": 22.00,
|
||||
"default": 1
|
||||
},
|
||||
"Italy VAT 10%":{
|
||||
"account_name": "IVA 10%",
|
||||
"tax_rate": 10.00
|
||||
},
|
||||
"Italy VAT 4%":{
|
||||
"account_name": "IVA 4%",
|
||||
"tax_rate": 4.00
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -577,8 +577,9 @@ class Item(WebsiteGenerator):
|
||||
# if barcode is getting updated , the row name has to reset.
|
||||
# Delete previous old row doc and re-enter row as if new to reset name in db.
|
||||
item_barcode.set("__islocal", True)
|
||||
item_barcode_entry_name = item_barcode.name
|
||||
item_barcode.name = None
|
||||
frappe.delete_doc("Item Barcode", item_barcode.name)
|
||||
frappe.delete_doc("Item Barcode", item_barcode_entry_name)
|
||||
|
||||
def validate_warehouse_for_reorder(self):
|
||||
'''Validate Reorder level table for duplicate and conditional mandatory'''
|
||||
|
@ -471,7 +471,7 @@ class TestItem(unittest.TestCase):
|
||||
item_doc = frappe.get_doc('Item', item_code)
|
||||
new_barcode = item_doc.append('barcodes')
|
||||
new_barcode.update(barcode_properties_list[0])
|
||||
self.assertRaises(frappe.DuplicateEntryError, item_doc.save)
|
||||
self.assertRaises(frappe.UniqueValidationError, item_doc.save)
|
||||
|
||||
# Add invalid barcode - should cause InvalidBarcode
|
||||
item_doc = frappe.get_doc('Item', item_code)
|
||||
|
@ -29,9 +29,18 @@ class ItemAttribute(Document):
|
||||
'''Validate that if there are existing items with attributes, they are valid'''
|
||||
attributes_list = [d.attribute_value for d in self.item_attribute_values]
|
||||
|
||||
for item in frappe.db.sql('''select i.name, iva.attribute_value as value
|
||||
from `tabItem Variant Attribute` iva, `tabItem` i where iva.attribute = %s
|
||||
and iva.parent = i.name and i.has_variants = 0''', self.name, as_dict=1):
|
||||
# Get Item Variant Attribute details of variant items
|
||||
items = frappe.db.sql("""
|
||||
select
|
||||
i.name, iva.attribute_value as value
|
||||
from
|
||||
`tabItem Variant Attribute` iva, `tabItem` i
|
||||
where
|
||||
iva.attribute = %(attribute)s
|
||||
and iva.parent = i.name and
|
||||
i.variant_of is not null and i.variant_of != ''""", {"attribute" : self.name}, as_dict=1)
|
||||
|
||||
for item in items:
|
||||
if self.numeric_values:
|
||||
validate_is_incremental(self, self.name, item.value, item.name)
|
||||
else:
|
||||
|
@ -50,16 +50,18 @@ class ItemPrice(Document):
|
||||
|
||||
def check_duplicates(self):
|
||||
conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s"
|
||||
condition_data_dict = dict(item_code=self.item_code, price_list=self.price_list, name=self.name)
|
||||
|
||||
for field in ['uom', 'valid_from',
|
||||
'valid_upto', 'packing_unit', 'customer', 'supplier']:
|
||||
if self.get(field):
|
||||
conditions += " and {0} = %({1})s".format(field, field)
|
||||
condition_data_dict[field] = self.get(field)
|
||||
|
||||
price_list_rate = frappe.db.sql("""
|
||||
SELECT price_list_rate
|
||||
FROM `tabItem Price`
|
||||
{conditions} """.format(conditions=conditions), self.as_dict())
|
||||
{conditions} """.format(conditions=conditions), condition_data_dict)
|
||||
|
||||
if price_list_rate :
|
||||
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty and Dates."), ItemPriceDuplicateItem)
|
||||
|
@ -280,7 +280,8 @@ frappe.ui.form.on('Material Request', {
|
||||
fieldname:'default_supplier',
|
||||
fieldtype: 'Link',
|
||||
options: 'Supplier',
|
||||
description: __('Select a Supplier from the Default Supplier List of the items below.'),
|
||||
description: __('Select a Supplier from the Default Suppliers of the items below. \
|
||||
On selection, a Purchase Order will be made against items belonging to the selected Supplier only.'),
|
||||
get_query: () => {
|
||||
return{
|
||||
query: "erpnext.stock.doctype.material_request.material_request.get_default_supplier_query",
|
||||
|
@ -13,7 +13,9 @@
|
||||
"material_request_type",
|
||||
"transfer_status",
|
||||
"customer",
|
||||
"status",
|
||||
"column_break_2",
|
||||
"transaction_date",
|
||||
"schedule_date",
|
||||
"company",
|
||||
"amended_from",
|
||||
@ -25,11 +27,8 @@
|
||||
"scan_barcode",
|
||||
"items",
|
||||
"more_info",
|
||||
"requested_by",
|
||||
"transaction_date",
|
||||
"column_break2",
|
||||
"status",
|
||||
"per_ordered",
|
||||
"column_break2",
|
||||
"per_received",
|
||||
"printing_details",
|
||||
"letter_head",
|
||||
@ -82,7 +81,8 @@
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"label": "Customer",
|
||||
"options": "Customer"
|
||||
"options": "Customer",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
@ -92,12 +92,12 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Required By"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Company",
|
||||
"oldfieldname": "company",
|
||||
@ -153,18 +153,10 @@
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-file-text"
|
||||
},
|
||||
{
|
||||
"fieldname": "requested_by",
|
||||
"fieldtype": "Data",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Requested For",
|
||||
"options": "Email"
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Transaction Date",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "transaction_date",
|
||||
@ -197,7 +189,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"depends_on": "eval:doc.per_ordered > 0",
|
||||
"fieldname": "per_ordered",
|
||||
"fieldtype": "Percent",
|
||||
"label": "% Ordered",
|
||||
@ -208,7 +200,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"depends_on": "eval:doc.per_received > 0",
|
||||
"fieldname": "per_received",
|
||||
"fieldtype": "Percent",
|
||||
"label": "% Received",
|
||||
@ -282,13 +274,15 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "warehouse_section",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Warehouse"
|
||||
},
|
||||
{
|
||||
"description": "Sets 'For Warehouse' in each row of the Items table.",
|
||||
"description": "Sets 'Target Warehouse' in each row of the Items table.",
|
||||
"fieldname": "set_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set Warehouse",
|
||||
"in_list_view": 1,
|
||||
"label": "Set Target Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -300,9 +294,10 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.material_request_type == 'Material Transfer'",
|
||||
"description": "Sets 'Source Warehouse' in each row of the Items table.",
|
||||
"fieldname": "set_from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set From Warehouse",
|
||||
"label": "Set Source Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -319,7 +314,7 @@
|
||||
"idx": 70,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-10 13:27:54.891058",
|
||||
"modified": "2020-09-19 01:04:09.285862",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
|
@ -7,7 +7,7 @@ def get_data():
|
||||
'fieldname': 'material_request',
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Related'),
|
||||
'label': _('Reference'),
|
||||
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order']
|
||||
},
|
||||
{
|
||||
|
@ -13,12 +13,10 @@
|
||||
"schedule_date",
|
||||
"section_break_4",
|
||||
"description",
|
||||
"column_break_12",
|
||||
"item_group",
|
||||
"brand",
|
||||
"image_section",
|
||||
"image",
|
||||
"column_break_12",
|
||||
"manufacturer_part_no",
|
||||
"quantity_and_warehouse",
|
||||
"qty",
|
||||
"stock_uom",
|
||||
@ -28,12 +26,26 @@
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"stock_qty",
|
||||
"qty_info_sec_break",
|
||||
"min_order_qty",
|
||||
"projected_qty",
|
||||
"qty_info_col_break",
|
||||
"actual_qty",
|
||||
"ordered_qty",
|
||||
"received_qty",
|
||||
"rate_and_amount_section_break",
|
||||
"rate",
|
||||
"col_break3",
|
||||
"amount",
|
||||
"manufacture_details",
|
||||
"manufacturer",
|
||||
"manufacturer_part_no",
|
||||
"col_break_mfg",
|
||||
"bom_no",
|
||||
"accounting_dimensions_section",
|
||||
"project",
|
||||
"dimension_col_break",
|
||||
"cost_center",
|
||||
"more_info",
|
||||
"lead_time_date",
|
||||
"sales_order",
|
||||
@ -41,19 +53,7 @@
|
||||
"production_plan",
|
||||
"material_request_plan_item",
|
||||
"col_break4",
|
||||
"min_order_qty",
|
||||
"projected_qty",
|
||||
"actual_qty",
|
||||
"ordered_qty",
|
||||
"received_qty",
|
||||
"accounting_details",
|
||||
"expense_account",
|
||||
"accounting_dimensions_section",
|
||||
"project",
|
||||
"dimension_col_break",
|
||||
"cost_center",
|
||||
"section_break_37",
|
||||
"bom_no",
|
||||
"section_break_46",
|
||||
"page_break"
|
||||
],
|
||||
@ -164,7 +164,7 @@
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "For Warehouse",
|
||||
"label": "Target Warehouse",
|
||||
"oldfieldname": "warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
@ -191,12 +191,14 @@
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate"
|
||||
"label": "Rate",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@ -326,6 +328,7 @@
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"fieldname": "ordered_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Completed Qty",
|
||||
@ -335,12 +338,6 @@
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "accounting_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounting Details"
|
||||
},
|
||||
{
|
||||
"fieldname": "expense_account",
|
||||
"fieldtype": "Link",
|
||||
@ -367,21 +364,10 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "image_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Image"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.material_request_type == \"Manufacture\"",
|
||||
"fieldname": "section_break_37",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Manufacturing"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.docstatus==1",
|
||||
"fieldname": "received_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Received Quantity",
|
||||
"label": "Received Qty",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -398,6 +384,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:in_list([\"Manufacture\", \"Purchase\"], parent.material_request_type)",
|
||||
"fieldname": "manufacture_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Manufacture"
|
||||
@ -430,10 +417,11 @@
|
||||
"depends_on": "eval:parent.material_request_type == \"Material Transfer\"",
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Source Warehouse (Material Transfer)",
|
||||
"label": "Source Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "BOM No",
|
||||
@ -444,12 +432,25 @@
|
||||
{
|
||||
"fieldname": "section_break_46",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break_mfg",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "qty_info_sec_break",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "qty_info_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-15 09:00:00.992835",
|
||||
"modified": "2020-10-02 11:44:36.553064",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request Item",
|
||||
|
@ -180,18 +180,15 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC")
|
||||
|
||||
#stock raw materials in a warehouse before transfer
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
item_code="_Test Item Home Desktop 100", qty=1, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
item_code = "Test Extra Item 1", qty=1, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse - _TC",
|
||||
item_code = "_Test Item", qty=1, basic_rate=100)
|
||||
|
||||
item_code = "_Test FG Item", qty=1, basic_rate=100)
|
||||
rm_items = [
|
||||
{
|
||||
"item_code": item_code,
|
||||
"rm_item_code": po.supplied_items[0].rm_item_code,
|
||||
"item_name": "_Test Item",
|
||||
"item_name": "_Test FG Item",
|
||||
"qty": po.supplied_items[0].required_qty,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"stock_uom": "Nos"
|
||||
@ -203,14 +200,6 @@ class TestPurchaseReceipt(unittest.TestCase):
|
||||
"qty": po.supplied_items[1].required_qty,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"stock_uom": "Nos"
|
||||
},
|
||||
{
|
||||
"item_code": item_code,
|
||||
"rm_item_code": po.supplied_items[2].rm_item_code,
|
||||
"item_name": "_Test Item Home Desktop 100",
|
||||
"qty": po.supplied_items[2].required_qty,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"stock_uom": "Nos"
|
||||
}
|
||||
]
|
||||
rm_item_string = json.dumps(rm_items)
|
||||
|
@ -77,38 +77,33 @@ def get_price_list():
|
||||
return item_rate_map
|
||||
|
||||
def get_last_purchase_rate():
|
||||
|
||||
item_last_purchase_rate_map = {}
|
||||
|
||||
query = """select * from (select
|
||||
result.item_code,
|
||||
result.base_rate
|
||||
from (
|
||||
query = """select * from (
|
||||
(select
|
||||
po_item.item_code,
|
||||
po_item.item_name,
|
||||
po.transaction_date as posting_date,
|
||||
po_item.base_price_list_rate,
|
||||
po_item.discount_percentage,
|
||||
po_item.base_rate
|
||||
from `tabPurchase Order` po, `tabPurchase Order Item` po_item
|
||||
where po.name = po_item.parent and po.docstatus = 1)
|
||||
union
|
||||
(select
|
||||
pr_item.item_code,
|
||||
pr_item.item_name,
|
||||
pr.posting_date,
|
||||
pr_item.base_price_list_rate,
|
||||
pr_item.discount_percentage,
|
||||
pr_item.base_rate
|
||||
from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
|
||||
where pr.name = pr_item.parent and pr.docstatus = 1)
|
||||
) result
|
||||
order by result.item_code asc, result.posting_date desc) result_wrapper
|
||||
group by item_code"""
|
||||
union
|
||||
(select
|
||||
pi_item.item_code,
|
||||
pi.posting_date,
|
||||
pi_item.base_rate
|
||||
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
|
||||
where pi.name = pi_item.parent and pi.docstatus = 1 and pi.update_stock = 1)
|
||||
) result order by result.item_code asc, result.posting_date asc"""
|
||||
|
||||
for d in frappe.db.sql(query, as_dict=1):
|
||||
item_last_purchase_rate_map.setdefault(d.item_code, d.base_rate)
|
||||
item_last_purchase_rate_map[d.item_code] = d.base_rate
|
||||
|
||||
return item_last_purchase_rate_map
|
||||
|
||||
|
@ -316,12 +316,12 @@ Authorized Signatory,المخول بالتوقيع,
|
||||
Auto Material Requests Generated,إنشاء طلب مواد تلقائي,
|
||||
Auto Repeat,تكرار تلقائي,
|
||||
Auto repeat document updated,تكرار تلقائي للمستندات المحدثة,
|
||||
Automotive,سيارات,
|
||||
Automotive,سيارات,متحرك بطاقة ذاتية
|
||||
Available,متاح,
|
||||
Available Leaves,المغادارت المتوفرة,
|
||||
Available Leaves,المغادارت والاجازات المتاحة,
|
||||
Available Qty,الكمية المتاحة,
|
||||
Available Selling,المبيعات المتاحة,
|
||||
Available for use date is required,مطلوب متاح لتاريخ الاستخدام,
|
||||
Available for use date is required,مطلوب تاريخ متاح للاستخدام,
|
||||
Available slots,الفتحات المتاحة,
|
||||
Available {0},متاح {0},
|
||||
Available-for-use Date should be after purchase date,يجب أن يكون التاريخ متاحًا بعد تاريخ الشراء,
|
||||
@ -331,16 +331,16 @@ Avg Daily Outgoing,متوسط الصادرات اليومية,
|
||||
Avg. Buying Price List Rate,متوسط قائمة أسعار الشراء,
|
||||
Avg. Selling Price List Rate,متوسط قائمة أسعار البيع,
|
||||
Avg. Selling Rate,متوسط معدل البيع,
|
||||
BOM,فاتورة المواد,
|
||||
BOM Browser,BOM متصفح,
|
||||
BOM No,رقم قائمة المواد,
|
||||
BOM Rate,سعر قائمة المواد,
|
||||
BOM Stock Report,تقرير مخزون فاتورة المواد,
|
||||
BOM,قائمة مكونات المواد,
|
||||
BOM Browser,قائمة مكونات المواد متصفح,
|
||||
BOM No,رقم قائمة مكونات المواد,
|
||||
BOM Rate,سعر او معدل قائمة مكونات المواد,
|
||||
BOM Stock Report,تقرير مخزون قائمة مكونات المواد,
|
||||
BOM and Manufacturing Quantity are required,مطلوب، قائمة مكونات المواد و كمية التصنيع,
|
||||
BOM does not contain any stock item,فاتورة الموارد لا تحتوي على أي صنف مخزون,
|
||||
BOM {0} does not belong to Item {1},قائمة المواد {0} لا تنتمي إلى الصنف {1},
|
||||
BOM {0} must be active,فاتورة المواد {0} يجب أن تكون نشطة\n<br>\nBOM {0} must be active,
|
||||
BOM {0} must be submitted,فاتورة المواد {0} يجب أن تكون مسجلة\n<br>\nBOM {0} must be submitted,
|
||||
BOM does not contain any stock item,قائمة مكونات المواد لا تحتوي على أي صنف مخزون,
|
||||
BOM {0} does not belong to Item {1},قائمة مكونات المواد {0} لا تنتمي إلى الصنف {1},
|
||||
BOM {0} must be active,قائمة مكونات المواد {0} يجب أن تكون نشطة\n<br>\nBOM {0} must be active,
|
||||
BOM {0} must be submitted,قائمة مكونات المواد {0} يجب أن تكون مسجلة\n<br>\nBOM {0} must be submitted,
|
||||
Balance,الموازنة,
|
||||
Balance (Dr - Cr),الرصيد (مدين - دائن),
|
||||
Balance ({0}),الرصيد ({0}),
|
||||
@ -389,23 +389,23 @@ Bill Date,تاريخ الفاتورة,
|
||||
Bill No,رقم الفاتورة,
|
||||
Bill of Materials,فاتورة المواد,
|
||||
Bill of Materials (BOM),قوائم المواد,
|
||||
Billable Hours,ساعات للفوترة,
|
||||
Billed,توصف,
|
||||
Billable Hours,ساعات قابلة للفوترة,
|
||||
Billed,تمت الفوترة,
|
||||
Billed Amount,القيمة المقدم فاتورة بها,
|
||||
Billing,الفواتير,
|
||||
Billing Address,عنوان تقديم الفواتير,
|
||||
Billing Address,العنوان الذي ترسل به الفواتير,
|
||||
Billing Address is same as Shipping Address,عنوان الفواتير هو نفس عنوان الشحن,
|
||||
Billing Amount,قيمة الفواتير,
|
||||
Billing Status,الحالة الفواتير,
|
||||
Billing Status,حالة الفواتير,
|
||||
Billing currency must be equal to either default company's currency or party account currency,يجب أن تكون عملة الفوترة مساوية لعملة الشركة الافتراضية أو عملة حساب الطرف,
|
||||
Bills raised by Suppliers.,فواتير حولت من قبل الموردين.,
|
||||
Bills raised to Customers.,فواتير حولت للزبائن.,
|
||||
Biotechnology,التكنولوجيا الحيوية,
|
||||
Birthday Reminder,تذكير عيد ميلاد,
|
||||
Black,أسود,
|
||||
Blanket Orders from Costumers.,أوامر بطانية من العملاء.,
|
||||
Blanket Orders from Costumers.,أوامر شراء شاملة من العملاء.,
|
||||
Block Invoice,حظر الفاتورة,
|
||||
Boms,قوائم المواد,
|
||||
Boms,قوائم مكونات المواد,
|
||||
Bonus Payment Date cannot be a past date,لا يمكن أن يكون تاريخ الدفع المكافأ تاريخًا سابقًا,
|
||||
Both Trial Period Start Date and Trial Period End Date must be set,يجب تعيين كل من تاريخ بدء الفترة التجريبية وتاريخ انتهاء الفترة التجريبية,
|
||||
Both Warehouse must belong to same Company,يجب أن ينتمي المستودع إلى نفس الشركة\n<br>\nBoth Warehouse must belong to same Company,
|
||||
@ -504,9 +504,9 @@ Cash In Hand,النقدية الحاضرة,
|
||||
Cash or Bank Account is mandatory for making payment entry,الحساب النقدي أو البنكي مطلوب لعمل مدخل بيع <br>Cash or Bank Account is mandatory for making payment entry,
|
||||
Cashier Closing,إغلاق أمين الصندوق,
|
||||
Casual Leave,أجازة عادية,
|
||||
Category,فئة,
|
||||
Category,فئة,صنف
|
||||
Category Name,اسم التصنيف,
|
||||
Caution,الحذر,
|
||||
Caution,الحذر,تحذير
|
||||
Central Tax,الضريبة المركزية,
|
||||
Certification,شهادة,
|
||||
Cess,سيس,
|
||||
@ -627,7 +627,7 @@ Cost Center with existing transactions can not be converted to ledger,مركز
|
||||
Cost Centers,مراكز التكلفة,
|
||||
Cost Updated,تم تحديث التكلفة\n<br>\nCost Updated,
|
||||
Cost as on,التكلفة كما في,
|
||||
Cost of Delivered Items,تكلفة البنود المسلمة,
|
||||
Cost of Delivered Items,تكلفة السلع والمواد المسلمة,
|
||||
Cost of Goods Sold,تكلفة البضاعة المباعة,
|
||||
Cost of Issued Items,تكلفة المواد المصروفة,
|
||||
Cost of New Purchase,تكلفة الشراء الجديد,
|
||||
@ -1300,7 +1300,7 @@ Insurance Start date should be less than Insurance End date,يجب أن يكون
|
||||
Integrated Tax,ضريبة متكاملة,
|
||||
Inter-State Supplies,اللوازم بين الدول,
|
||||
Interest Amount,مبلغ الفائدة,
|
||||
Interests,الإهتمامات,
|
||||
Interests,الإهتمامات او الفوائد,
|
||||
Intern,المتدرب,
|
||||
Internet Publishing,نشر على شبكة الإنترنت,
|
||||
Intra-State Supplies,اللوازم داخل الدولة,
|
||||
@ -1421,13 +1421,13 @@ Lab Test UOM,اختبار مختبر أوم,
|
||||
Lab Tests and Vital Signs,اختبارات المختبر وعلامات حيوية,
|
||||
Lab result datetime cannot be before testing datetime,لا يمكن أن يكون تاريخ نتيجة المختبر سابقا لتاريخ الفحص,
|
||||
Lab testing datetime cannot be before collection datetime,لا يمكن أن يكون وقت اختبار المختبر قبل تاريخ جمع البيانات,
|
||||
Label,ملصق,
|
||||
Label,ملصق,'طابع
|
||||
Laboratory,مختبر,
|
||||
Language Name,اسم اللغة,
|
||||
Large,كبير,
|
||||
Last Communication,آخر الاتصالات,
|
||||
Last Communication Date,تاريخ الاتصال الأخير,
|
||||
Last Name,اسم العائلة,
|
||||
Last Name,اسم العائلة او اللقب,
|
||||
Last Order Amount,قيمة آخر طلب,
|
||||
Last Order Date,تاريخ أخر أمر بيع,
|
||||
Last Purchase Price,سعر الشراء الأخير,
|
||||
|
Can't render this file because it is too large.
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.share
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form
|
||||
from frappe.utils import cstr, now_datetime, cint, flt, get_time, get_datetime, get_link_to_form, date_diff, nowdate
|
||||
from erpnext.controllers.status_updater import StatusUpdater
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
@ -29,8 +29,28 @@ class TransactionBase(StatusUpdater):
|
||||
except ValueError:
|
||||
frappe.throw(_('Invalid Posting Time'))
|
||||
|
||||
self.validate_future_posting()
|
||||
self.validate_with_last_transaction_posting_time()
|
||||
|
||||
def is_stock_transaction(self):
|
||||
if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
|
||||
"Delivery Note", "Purchase Receipt", "Fees"]:
|
||||
return False
|
||||
|
||||
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if not (self.get("update_stock") or self.get("is_pos")):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def validate_future_posting(self):
|
||||
if not self.is_stock_transaction():
|
||||
return
|
||||
|
||||
if getattr(self, 'set_posting_time', None) and date_diff(self.posting_date, nowdate()) > 0:
|
||||
msg = _("Posting future transactions are not allowed due to Immutable Ledger")
|
||||
frappe.throw(msg, title=_("Future Posting Not Allowed"))
|
||||
|
||||
def add_calendar_event(self, opts, force=False):
|
||||
if cstr(self.contact_by) != cstr(self._prev.contact_by) or \
|
||||
cstr(self.contact_date) != cstr(self._prev.contact_date) or force or \
|
||||
@ -162,12 +182,7 @@ class TransactionBase(StatusUpdater):
|
||||
|
||||
def validate_with_last_transaction_posting_time(self):
|
||||
|
||||
if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
|
||||
"Delivery Note", "Purchase Receipt", "Fees"]:
|
||||
return
|
||||
|
||||
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if not (self.get("update_stock") or self.get("is_pos")):
|
||||
if not self.is_stock_transaction():
|
||||
return
|
||||
|
||||
for item in self.get('items'):
|
||||
|
Loading…
Reference in New Issue
Block a user