mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-24 21:25:04 +00:00
Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6faf459f97 | ||
|
|
40058c2617 | ||
|
|
78ad0eaa74 | ||
|
|
b12bd5f558 | ||
|
|
be8a22d33b | ||
|
|
757c168a8d | ||
|
|
b0b00dc869 | ||
|
|
09b92fd78c | ||
|
|
f1a1fc6c5b | ||
|
|
8f26d62b35 | ||
|
|
8e9d72bd2e | ||
|
|
7461239218 | ||
|
|
2f356dcc6c | ||
|
|
4640febc83 | ||
|
|
c53dc06f80 | ||
|
|
a981b79865 | ||
|
|
050339c88e | ||
|
|
461780da22 | ||
|
|
e8db543028 | ||
|
|
79fd38cf3f | ||
|
|
dfb1e105bd | ||
|
|
2e7043ca90 | ||
|
|
7c8194a1a8 | ||
|
|
15f5e8d4ff | ||
|
|
3738ea5794 | ||
|
|
c11aebaaae | ||
|
|
0cf6144b3f | ||
|
|
41a7d3fd60 | ||
|
|
2b0b24f7c3 | ||
|
|
f72fc73913 | ||
|
|
8a331e0f26 | ||
|
|
da913d49a7 | ||
|
|
21aea52c32 | ||
|
|
9b90323d53 | ||
|
|
994272b966 | ||
|
|
fee4cd5f40 | ||
|
|
3023dbbe95 | ||
|
|
cc21241887 | ||
|
|
66fdd1c13f | ||
|
|
5723a200c5 | ||
|
|
35c9493336 | ||
|
|
b235b95bed | ||
|
|
50abbded34 | ||
|
|
31b479d71f | ||
|
|
769736ffea | ||
|
|
b79ddbbf60 | ||
|
|
1c5e36c7b6 | ||
|
|
eaf577f078 | ||
|
|
e02ad91c39 | ||
|
|
d4a6035c83 | ||
|
|
99226d3811 | ||
|
|
a1f7e35914 | ||
|
|
915c4819b6 | ||
|
|
d817c50581 | ||
|
|
8ba1e0f31e | ||
|
|
3ad5d676ab | ||
|
|
05d24e3665 | ||
|
|
3c0cc024aa | ||
|
|
88c8c36805 | ||
|
|
7c4a9b56ff | ||
|
|
16e554dd7b | ||
|
|
3896d41e95 | ||
|
|
12625d87b0 | ||
|
|
cbb8dd6aa6 | ||
|
|
9372d46c08 | ||
|
|
dff61ab759 | ||
|
|
551190af30 | ||
|
|
2c54e763e4 | ||
|
|
6b866e24f6 | ||
|
|
be2990ec88 | ||
|
|
c0f7f7da42 | ||
|
|
b34c78c4e7 | ||
|
|
4ada090cb2 | ||
|
|
5677f25215 | ||
|
|
f1687cfb14 | ||
|
|
a00459aec3 | ||
|
|
8757435898 | ||
|
|
dcf62dc548 | ||
|
|
b835760b0b | ||
|
|
bd0c20f789 | ||
|
|
5ef98fcea1 | ||
|
|
5e28d0234e | ||
|
|
5e03a4e9e2 | ||
|
|
e98e64f925 | ||
|
|
9ce281d008 | ||
|
|
27a3f2ce55 | ||
|
|
61858a60c2 | ||
|
|
bb5eeb6bd6 | ||
|
|
3494c9ccb6 | ||
|
|
a0df23415b | ||
|
|
8510c398a4 | ||
|
|
bae476cc99 | ||
|
|
4dfc660cc0 | ||
|
|
f47be46717 | ||
|
|
8d9305ee5f | ||
|
|
6819f0106d | ||
|
|
e3de229b82 | ||
|
|
3e67994cc7 |
22
CODEOWNERS
22
CODEOWNERS
@@ -3,13 +3,13 @@
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. Unless a later match takes precedence,
|
||||
|
||||
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/assets/ @anandbaburajan @deepeshgarg007
|
||||
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
|
||||
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
||||
pos* @nextchamp-saqib
|
||||
erpnext/loan_management/ @deepeshgarg007
|
||||
erpnext/regional @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/selling @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/support/ @deepeshgarg007
|
||||
pos*
|
||||
|
||||
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||
@@ -18,12 +18,8 @@ erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/subcontracting @rohitwaghchaure @s-aga-r
|
||||
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
erpnext/projects/ @ruchamahabal
|
||||
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
|
||||
erpnext/patches/ @deepeshgarg007
|
||||
|
||||
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
|
||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib
|
||||
|
||||
.github/ @ankush
|
||||
.github/ @deepeshgarg007
|
||||
pyproject.toml @ankush
|
||||
|
||||
@@ -2,7 +2,7 @@ import inspect
|
||||
|
||||
import frappe
|
||||
|
||||
__version__ = "14.20.3"
|
||||
__version__ = "14.22.1"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"root_type",
|
||||
"report_type",
|
||||
"account_currency",
|
||||
"inter_company_account",
|
||||
"column_break1",
|
||||
"parent_account",
|
||||
"account_type",
|
||||
@@ -34,15 +33,11 @@
|
||||
{
|
||||
"fieldname": "properties",
|
||||
"fieldtype": "Section Break",
|
||||
"oldfieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
@@ -53,9 +48,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "account_name",
|
||||
"oldfieldtype": "Data",
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "account_number",
|
||||
@@ -63,17 +56,13 @@
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Account Number",
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_group",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Group",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Is Group"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
@@ -85,9 +74,7 @@
|
||||
"options": "Company",
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "root_type",
|
||||
@@ -95,9 +82,7 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Root Type",
|
||||
"options": "\nAsset\nLiability\nIncome\nExpense\nEquity",
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "report_type",
|
||||
@@ -105,32 +90,18 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Report Type",
|
||||
"options": "\nBalance Sheet\nProfit and Loss",
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_group==0",
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "inter_company_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Inter Company Account",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
@@ -142,9 +113,7 @@
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Account",
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"description": "Setting Account Type helps in selecting this Account in transactions.",
|
||||
@@ -154,9 +123,7 @@
|
||||
"label": "Account Type",
|
||||
"oldfieldname": "account_type",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary"
|
||||
},
|
||||
{
|
||||
"description": "Rate at which this tax is applied",
|
||||
@@ -164,9 +131,7 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Rate",
|
||||
"oldfieldname": "tax_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"description": "If the account is frozen, entries are allowed to restricted users.",
|
||||
@@ -175,17 +140,13 @@
|
||||
"label": "Frozen",
|
||||
"oldfieldname": "freeze_account",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "No\nYes",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "No\nYes"
|
||||
},
|
||||
{
|
||||
"fieldname": "balance_must_be",
|
||||
"fieldtype": "Select",
|
||||
"label": "Balance must be",
|
||||
"options": "\nDebit\nCredit",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "\nDebit\nCredit"
|
||||
},
|
||||
{
|
||||
"fieldname": "lft",
|
||||
@@ -194,9 +155,7 @@
|
||||
"label": "Lft",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"search_index": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rgt",
|
||||
@@ -205,9 +164,7 @@
|
||||
"label": "Rgt",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"search_index": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "old_parent",
|
||||
@@ -215,33 +172,27 @@
|
||||
"hidden": 1,
|
||||
"label": "Old Parent",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:(doc.report_type == 'Profit and Loss' && !doc.is_group)",
|
||||
"fieldname": "include_in_gross",
|
||||
"fieldtype": "Check",
|
||||
"label": "Include in gross",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Include in gross"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"label": "Disable"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-11 15:15:54.338622",
|
||||
"modified": "2023-04-11 16:08:46.983677",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Account",
|
||||
@@ -301,5 +252,6 @@
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -182,6 +182,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Payment Terms from orders will be fetched into the invoices as is",
|
||||
"fieldname": "automatically_fetch_payment_terms",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Fetch Payment Terms from Order"
|
||||
@@ -362,7 +363,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-03-28 09:50:20.375233",
|
||||
"modified": "2023-04-14 17:22:03.680886",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -287,10 +287,6 @@ class TestJournalEntry(unittest.TestCase):
|
||||
jv.submit()
|
||||
|
||||
def test_inter_company_jv(self):
|
||||
frappe.db.set_value("Account", "Sales Expenses - _TC", "inter_company_account", 1)
|
||||
frappe.db.set_value("Account", "Buildings - _TC", "inter_company_account", 1)
|
||||
frappe.db.set_value("Account", "Sales Expenses - _TC1", "inter_company_account", 1)
|
||||
frappe.db.set_value("Account", "Buildings - _TC1", "inter_company_account", 1)
|
||||
jv = make_journal_entry(
|
||||
"Sales Expenses - _TC",
|
||||
"Buildings - _TC",
|
||||
|
||||
@@ -971,29 +971,47 @@ frappe.ui.form.on('Payment Entry', {
|
||||
},
|
||||
callback: function(r, rt) {
|
||||
if(r.message) {
|
||||
var write_off_row = $.map(frm.doc["deductions"] || [], function(t) {
|
||||
const write_off_row = $.map(frm.doc["deductions"] || [], function(t) {
|
||||
return t.account==r.message[account] ? t : null; });
|
||||
|
||||
var row = [];
|
||||
|
||||
var difference_amount = flt(frm.doc.difference_amount,
|
||||
const difference_amount = flt(frm.doc.difference_amount,
|
||||
precision("difference_amount"));
|
||||
|
||||
if (!write_off_row.length && difference_amount) {
|
||||
row = frm.add_child("deductions");
|
||||
row.account = r.message[account];
|
||||
row.cost_center = r.message["cost_center"];
|
||||
} else {
|
||||
row = write_off_row[0];
|
||||
}
|
||||
const add_deductions = (details) => {
|
||||
if (!write_off_row.length && difference_amount) {
|
||||
row = frm.add_child("deductions");
|
||||
row.account = details[account];
|
||||
row.cost_center = details["cost_center"];
|
||||
} else {
|
||||
row = write_off_row[0];
|
||||
}
|
||||
|
||||
if (row) {
|
||||
row.amount = flt(row.amount) + difference_amount;
|
||||
} else {
|
||||
frappe.msgprint(__("No gain or loss in the exchange rate"))
|
||||
}
|
||||
if (row) {
|
||||
row.amount = flt(row.amount) + difference_amount;
|
||||
} else {
|
||||
frappe.msgprint(__("No gain or loss in the exchange rate"))
|
||||
}
|
||||
refresh_field("deductions");
|
||||
};
|
||||
|
||||
refresh_field("deductions");
|
||||
if (!r.message[account]) {
|
||||
frappe.prompt({
|
||||
label: __("Please Specify Account"),
|
||||
fieldname: account,
|
||||
fieldtype: "Link",
|
||||
options: "Account",
|
||||
get_query: () => ({
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
}
|
||||
})
|
||||
}, (values) => {
|
||||
const details = Object.assign({}, r.message, values);
|
||||
add_deductions(details);
|
||||
}, __(frappe.unscrub(account)));
|
||||
} else {
|
||||
add_deductions(r.message);
|
||||
}
|
||||
|
||||
frm.events.set_unallocated_amount(frm);
|
||||
}
|
||||
|
||||
@@ -1594,17 +1594,7 @@ def get_account_details(account, date, cost_center=None):
|
||||
@frappe.whitelist()
|
||||
def get_company_defaults(company):
|
||||
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
|
||||
ret = frappe.get_cached_value("Company", company, fields, as_dict=1)
|
||||
|
||||
for fieldname in fields:
|
||||
if not ret[fieldname]:
|
||||
frappe.throw(
|
||||
_("Please set default {0} in Company {1}").format(
|
||||
frappe.get_meta("Company").get_label(fieldname), company
|
||||
)
|
||||
)
|
||||
|
||||
return ret
|
||||
return frappe.get_cached_value("Company", company, fields, as_dict=1)
|
||||
|
||||
|
||||
def get_outstanding_on_journal_entry(name):
|
||||
@@ -1764,7 +1754,12 @@ def get_payment_entry(
|
||||
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
|
||||
frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
|
||||
else:
|
||||
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value(
|
||||
if doc.doctype in (
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"Purchase Order",
|
||||
"Sales Order",
|
||||
) and frappe.get_cached_value(
|
||||
"Payment Terms Template",
|
||||
{"name": doc.payment_terms_template},
|
||||
"allocate_payment_based_on_payment_terms",
|
||||
|
||||
@@ -24,7 +24,6 @@ class TestGeneralLedger(FrappeTestCase):
|
||||
"root_type": "Asset",
|
||||
"report_type": "Balance Sheet",
|
||||
"account_currency": "USD",
|
||||
"inter_company_account": 0,
|
||||
"parent_account": "Bank Accounts - _TC",
|
||||
"account_type": "Bank",
|
||||
"doctype": "Account",
|
||||
|
||||
@@ -36,7 +36,7 @@ frappe.listview_settings['Asset'] = {
|
||||
}
|
||||
},
|
||||
onload: function(me) {
|
||||
me.page.add_action_item('Make Asset Movement', function() {
|
||||
me.page.add_action_item(__("Make Asset Movement"), function() {
|
||||
const assets = me.get_checked_items();
|
||||
frappe.call({
|
||||
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
|
||||
|
||||
@@ -24,7 +24,7 @@ frappe.query_reports["Fixed Asset Register"] = {
|
||||
"label": __("Period Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Fiscal Year", "Date Range"],
|
||||
"default": ["Fiscal Year"],
|
||||
"default": "Fiscal Year",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -75,12 +75,6 @@ frappe.query_reports["Fixed Asset Register"] = {
|
||||
fieldtype: "Link",
|
||||
options: "Asset Category"
|
||||
},
|
||||
{
|
||||
fieldname:"finance_book",
|
||||
label: __("Finance Book"),
|
||||
fieldtype: "Link",
|
||||
options: "Finance Book"
|
||||
},
|
||||
{
|
||||
fieldname:"cost_center",
|
||||
label: __("Cost Center"),
|
||||
@@ -96,8 +90,20 @@ frappe.query_reports["Fixed Asset Register"] = {
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname:"is_existing_asset",
|
||||
label: __("Is Existing Asset"),
|
||||
fieldname:"finance_book",
|
||||
label: __("Finance Book"),
|
||||
fieldtype: "Link",
|
||||
options: "Finance Book",
|
||||
depends_on: "eval: doc.only_depreciable_assets == 1",
|
||||
},
|
||||
{
|
||||
fieldname:"only_depreciable_assets",
|
||||
label: __("Only depreciable assets"),
|
||||
fieldtype: "Check"
|
||||
},
|
||||
{
|
||||
fieldname:"only_existing_assets",
|
||||
label: __("Only existing assets"),
|
||||
fieldtype: "Check"
|
||||
},
|
||||
]
|
||||
|
||||
@@ -45,8 +45,10 @@ def get_conditions(filters):
|
||||
filters.year_end_date = getdate(fiscal_year.year_end_date)
|
||||
|
||||
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
|
||||
if filters.get("is_existing_asset"):
|
||||
conditions["is_existing_asset"] = filters.get("is_existing_asset")
|
||||
if filters.get("only_depreciable_assets"):
|
||||
conditions["calculate_depreciation"] = filters.get("only_depreciable_assets")
|
||||
if filters.get("only_existing_assets"):
|
||||
conditions["is_existing_asset"] = filters.get("only_existing_assets")
|
||||
if filters.get("asset_category"):
|
||||
conditions["asset_category"] = filters.get("asset_category")
|
||||
if filters.get("cost_center"):
|
||||
@@ -102,19 +104,18 @@ def get_data(filters):
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
assets_linked_to_fb = frappe.db.get_all(
|
||||
doctype="Asset Finance Book",
|
||||
filters={"finance_book": filters.finance_book or ("is", "not set")},
|
||||
pluck="parent",
|
||||
)
|
||||
assets_linked_to_fb = None
|
||||
|
||||
if filters.only_depreciable_assets:
|
||||
assets_linked_to_fb = frappe.db.get_all(
|
||||
doctype="Asset Finance Book",
|
||||
filters={"finance_book": filters.finance_book or ("is", "not set")},
|
||||
pluck="parent",
|
||||
)
|
||||
|
||||
for asset in assets_record:
|
||||
if filters.finance_book:
|
||||
if asset.asset_id not in assets_linked_to_fb:
|
||||
continue
|
||||
else:
|
||||
if asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
|
||||
continue
|
||||
if assets_linked_to_fb and asset.asset_id not in assets_linked_to_fb:
|
||||
continue
|
||||
|
||||
asset_value = get_asset_value_after_depreciation(asset.asset_id, filters.finance_book)
|
||||
row = {
|
||||
@@ -172,11 +173,11 @@ def prepare_chart_data(data, filters):
|
||||
"datasets": [
|
||||
{
|
||||
"name": _("Asset Value"),
|
||||
"values": [d.get("asset_value") for d in labels_values_map.values()],
|
||||
"values": [flt(d.get("asset_value"), 2) for d in labels_values_map.values()],
|
||||
},
|
||||
{
|
||||
"name": _("Depreciatied Amount"),
|
||||
"values": [d.get("depreciated_amount") for d in labels_values_map.values()],
|
||||
"values": [flt(d.get("depreciated_amount"), 2) for d in labels_values_map.values()],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -310,7 +311,7 @@ def get_columns(filters):
|
||||
|
||||
return [
|
||||
{
|
||||
"label": _("Asset Id"),
|
||||
"label": _("Asset ID"),
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "asset_id",
|
||||
"options": "Asset",
|
||||
|
||||
@@ -495,6 +495,7 @@
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Items",
|
||||
"oldfieldname": "po_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Purchase Order Item",
|
||||
@@ -1100,8 +1101,7 @@
|
||||
{
|
||||
"fieldname": "before_items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"label": "Items"
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items_col_break",
|
||||
@@ -1271,7 +1271,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-28 18:59:16.322824",
|
||||
"modified": "2023-04-14 16:42:29.448464",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -310,7 +310,6 @@
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"label": "Items",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-shopping-cart"
|
||||
},
|
||||
@@ -318,6 +317,7 @@
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Items",
|
||||
"oldfieldname": "po_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Supplier Quotation Item",
|
||||
@@ -844,7 +844,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-12 18:35:39.740974",
|
||||
"modified": "2023-04-14 16:43:41.714832",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
||||
@@ -273,8 +273,8 @@ class AccountsController(TransactionBase):
|
||||
self.validate_payment_schedule_dates()
|
||||
self.set_due_date()
|
||||
self.set_payment_schedule()
|
||||
self.validate_payment_schedule_amount()
|
||||
if not self.get("ignore_default_payment_terms_template"):
|
||||
self.validate_payment_schedule_amount()
|
||||
self.validate_due_date()
|
||||
self.validate_advance_entries()
|
||||
|
||||
@@ -515,7 +515,6 @@ class AccountsController(TransactionBase):
|
||||
parent_dict.update({"customer": parent_dict.get("party_name")})
|
||||
|
||||
self.pricing_rules = []
|
||||
basic_item_details_map = {}
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get("item_code"):
|
||||
@@ -535,17 +534,7 @@ class AccountsController(TransactionBase):
|
||||
if self.get("is_subcontracted"):
|
||||
args["is_subcontracted"] = self.is_subcontracted
|
||||
|
||||
basic_details = basic_item_details_map.get(item.item_code)
|
||||
ret, basic_item_details = get_item_details(
|
||||
args,
|
||||
self,
|
||||
for_validate=True,
|
||||
overwrite_warehouse=False,
|
||||
return_basic_details=True,
|
||||
basic_details=basic_details,
|
||||
)
|
||||
|
||||
basic_item_details_map.setdefault(item.item_code, basic_item_details)
|
||||
ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
|
||||
|
||||
for fieldname, value in ret.items():
|
||||
if item.meta.get_field(fieldname) and value is not None:
|
||||
@@ -1618,6 +1607,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
automatically_fetch_payment_terms = 0
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
base_grand_total = base_grand_total - flt(self.base_write_off_amount)
|
||||
@@ -1663,19 +1653,20 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
self.append("payment_schedule", data)
|
||||
|
||||
for d in self.get("payment_schedule"):
|
||||
if d.invoice_portion:
|
||||
d.payment_amount = flt(
|
||||
grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
|
||||
)
|
||||
d.base_payment_amount = flt(
|
||||
base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
|
||||
)
|
||||
d.outstanding = d.payment_amount
|
||||
elif not d.invoice_portion:
|
||||
d.base_payment_amount = flt(
|
||||
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
|
||||
)
|
||||
if not automatically_fetch_payment_terms:
|
||||
for d in self.get("payment_schedule"):
|
||||
if d.invoice_portion:
|
||||
d.payment_amount = flt(
|
||||
grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
|
||||
)
|
||||
d.base_payment_amount = flt(
|
||||
base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
|
||||
)
|
||||
d.outstanding = d.payment_amount
|
||||
elif not d.invoice_portion:
|
||||
d.base_payment_amount = flt(
|
||||
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
|
||||
)
|
||||
|
||||
def get_order_details(self):
|
||||
if self.doctype == "Sales Invoice":
|
||||
@@ -1728,6 +1719,10 @@ class AccountsController(TransactionBase):
|
||||
"invoice_portion": schedule.invoice_portion,
|
||||
"mode_of_payment": schedule.mode_of_payment,
|
||||
"description": schedule.description,
|
||||
"payment_amount": schedule.payment_amount,
|
||||
"base_payment_amount": schedule.base_payment_amount,
|
||||
"outstanding": schedule.outstanding,
|
||||
"paid_amount": schedule.paid_amount,
|
||||
}
|
||||
|
||||
if schedule.discount_type == "Percentage":
|
||||
|
||||
@@ -859,6 +859,8 @@ def is_reposting_pending():
|
||||
|
||||
def future_sle_exists(args, sl_entries=None):
|
||||
key = (args.voucher_type, args.voucher_no)
|
||||
if not hasattr(frappe.local, "future_sle"):
|
||||
frappe.local.future_sle = {}
|
||||
|
||||
if validate_future_sle_not_exists(args, key, sl_entries):
|
||||
return False
|
||||
@@ -892,6 +894,9 @@ def future_sle_exists(args, sl_entries=None):
|
||||
)
|
||||
|
||||
for d in data:
|
||||
if key not in frappe.local.future_sle:
|
||||
frappe.local.future_sle[key] = frappe._dict({})
|
||||
|
||||
frappe.local.future_sle[key][(d.item_code, d.warehouse)] = d.total_row
|
||||
|
||||
return len(data)
|
||||
@@ -903,6 +908,9 @@ def validate_future_sle_not_exists(args, key, sl_entries=None):
|
||||
item_key = (args.get("item_code"), args.get("warehouse"))
|
||||
|
||||
if not sl_entries and hasattr(frappe.local, "future_sle"):
|
||||
if key not in frappe.local.future_sle:
|
||||
return False
|
||||
|
||||
if not frappe.local.future_sle.get(key) or (
|
||||
item_key and item_key not in frappe.local.future_sle.get(key)
|
||||
):
|
||||
@@ -910,11 +918,8 @@ def validate_future_sle_not_exists(args, key, sl_entries=None):
|
||||
|
||||
|
||||
def get_cached_data(args, key):
|
||||
if not hasattr(frappe.local, "future_sle"):
|
||||
frappe.local.future_sle = {}
|
||||
|
||||
if key not in frappe.local.future_sle:
|
||||
frappe.local.future_sle[key] = frappe._dict({})
|
||||
return False
|
||||
|
||||
if args.get("item_code"):
|
||||
item_key = (args.get("item_code"), args.get("warehouse"))
|
||||
|
||||
@@ -315,6 +315,7 @@ class WebsiteItem(WebsiteGenerator):
|
||||
self.item_code, skip_quotation_creation=True
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
def copy_specification_from_item_group(self):
|
||||
self.set("website_specifications", [])
|
||||
if self.item_group:
|
||||
|
||||
@@ -30,10 +30,6 @@ doctype_js = {
|
||||
|
||||
override_doctype_class = {"Address": "erpnext.accounts.custom.address.ERPNextAddress"}
|
||||
|
||||
override_whitelisted_methods = {
|
||||
"frappe.www.contact.send_message": "erpnext.templates.utils.send_message"
|
||||
}
|
||||
|
||||
welcome_email = "erpnext.setup.utils.welcome_email"
|
||||
|
||||
# setup wizard
|
||||
|
||||
@@ -9,15 +9,14 @@
|
||||
"production_item_tab",
|
||||
"item",
|
||||
"company",
|
||||
"item_name",
|
||||
"uom",
|
||||
"quantity",
|
||||
"cb0",
|
||||
"is_active",
|
||||
"is_default",
|
||||
"allow_alternative_item",
|
||||
"set_rate_of_sub_assembly_item_based_on_bom",
|
||||
"project",
|
||||
"quantity",
|
||||
"image",
|
||||
"currency_detail",
|
||||
"rm_cost_as_per",
|
||||
@@ -27,6 +26,8 @@
|
||||
"column_break_ivyw",
|
||||
"currency",
|
||||
"conversion_rate",
|
||||
"materials_section",
|
||||
"items",
|
||||
"section_break_21",
|
||||
"operations_section_section",
|
||||
"with_operations",
|
||||
@@ -38,8 +39,6 @@
|
||||
"operating_cost_per_bom_quantity",
|
||||
"operations_section",
|
||||
"operations",
|
||||
"materials_section",
|
||||
"items",
|
||||
"scrap_section",
|
||||
"scrap_items_section",
|
||||
"scrap_items",
|
||||
@@ -59,6 +58,7 @@
|
||||
"total_cost",
|
||||
"base_total_cost",
|
||||
"more_info_tab",
|
||||
"item_name",
|
||||
"description",
|
||||
"column_break_27",
|
||||
"has_variants",
|
||||
@@ -192,6 +192,7 @@
|
||||
"options": "Quality Inspection Template"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "currency_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Cost Configuration"
|
||||
@@ -417,7 +418,7 @@
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "website_section",
|
||||
"fieldtype": "Section Break",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Website"
|
||||
},
|
||||
{
|
||||
@@ -482,7 +483,7 @@
|
||||
{
|
||||
"fieldname": "section_break_21",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Operations & Materials"
|
||||
"label": "Operations"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_23",
|
||||
@@ -605,7 +606,7 @@
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-02-13 17:31:37.504565",
|
||||
"modified": "2023-04-06 12:47:58.514795",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
|
||||
@@ -22,17 +22,13 @@
|
||||
"produced_qty",
|
||||
"process_loss_qty",
|
||||
"project",
|
||||
"serial_no_and_batch_for_finished_good_section",
|
||||
"has_serial_no",
|
||||
"has_batch_no",
|
||||
"column_break_17",
|
||||
"serial_no",
|
||||
"batch_size",
|
||||
"section_break_ndpq",
|
||||
"required_items",
|
||||
"work_order_configuration",
|
||||
"settings_section",
|
||||
"allow_alternative_item",
|
||||
"use_multi_level_bom",
|
||||
"column_break_18",
|
||||
"column_break_17",
|
||||
"skip_transfer",
|
||||
"from_wip_warehouse",
|
||||
"update_consumed_material_cost_in_project",
|
||||
@@ -42,9 +38,14 @@
|
||||
"column_break_12",
|
||||
"fg_warehouse",
|
||||
"scrap_warehouse",
|
||||
"serial_no_and_batch_for_finished_good_section",
|
||||
"has_serial_no",
|
||||
"has_batch_no",
|
||||
"column_break_18",
|
||||
"serial_no",
|
||||
"batch_size",
|
||||
"required_items_section",
|
||||
"materials_and_operations_tab",
|
||||
"required_items",
|
||||
"operations_section",
|
||||
"operations",
|
||||
"transfer_material_against",
|
||||
@@ -586,7 +587,11 @@
|
||||
{
|
||||
"fieldname": "materials_and_operations_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Materials & Operations"
|
||||
"label": "Operations"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_ndpq",
|
||||
"fieldtype": "Section Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cogs",
|
||||
@@ -594,7 +599,7 @@
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-03 14:16:35.427731",
|
||||
"modified": "2023-04-06 12:35:12.149827",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order",
|
||||
|
||||
@@ -1696,7 +1696,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
$.each(["company", "customer"], function(i, fieldname) {
|
||||
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && me.frm.doc.doctype != "Purchase Order") {
|
||||
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && !["Purchase Order","Purchase Invoice"].includes(me.frm.doc.doctype)) {
|
||||
if (!me.frm.doc[fieldname]) {
|
||||
frappe.msgprint(__("Please specify") + ": " +
|
||||
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
|
||||
@@ -1920,7 +1920,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
}
|
||||
|
||||
prompt_user_for_reference_date(){
|
||||
var me = this;
|
||||
let me = this;
|
||||
frappe.prompt({
|
||||
label: __("Cheque/Reference Date"),
|
||||
fieldname: "reference_date",
|
||||
@@ -1947,7 +1947,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
||||
let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length;
|
||||
if(!is_eligible || !has_payment_schedule) return false;
|
||||
|
||||
let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date);
|
||||
let has_discount = this.frm.doc.payment_schedule.some(row => row.discount);
|
||||
return has_discount;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
|
||||
if(!window.erpnext) window.erpnext = {};
|
||||
|
||||
// Add / update a new Lead / Communication
|
||||
// subject, sender, description
|
||||
frappe.send_message = function(opts, btn) {
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
method: "erpnext.templates.utils.send_message",
|
||||
btn: btn,
|
||||
args: opts,
|
||||
callback: opts.callback
|
||||
});
|
||||
};
|
||||
|
||||
erpnext.subscribe_to_newsletter = function(opts, btn) {
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
@@ -12,3 +24,6 @@ erpnext.subscribe_to_newsletter = function(opts, btn) {
|
||||
callback: opts.callback
|
||||
});
|
||||
}
|
||||
|
||||
// for backward compatibility
|
||||
erpnext.send_message = frappe.send_message;
|
||||
|
||||
@@ -416,7 +416,6 @@
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"label": "Items",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-shopping-cart"
|
||||
},
|
||||
@@ -424,6 +423,7 @@
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Items",
|
||||
"oldfieldname": "quotation_details",
|
||||
"oldfieldtype": "Table",
|
||||
"options": "Quotation Item",
|
||||
@@ -1072,7 +1072,7 @@
|
||||
"idx": 82,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-12 18:32:28.671332",
|
||||
"modified": "2023-04-14 16:50:44.550098",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
|
||||
@@ -168,7 +168,7 @@ class Analytics(object):
|
||||
def get_sales_transactions_based_on_items(self):
|
||||
|
||||
if self.filters["value_quantity"] == "Value":
|
||||
value_field = "base_amount"
|
||||
value_field = "base_net_amount"
|
||||
else:
|
||||
value_field = "stock_qty"
|
||||
|
||||
@@ -216,7 +216,7 @@ class Analytics(object):
|
||||
|
||||
def get_sales_transactions_based_on_item_group(self):
|
||||
if self.filters["value_quantity"] == "Value":
|
||||
value_field = "base_amount"
|
||||
value_field = "base_net_amount"
|
||||
else:
|
||||
value_field = "qty"
|
||||
|
||||
|
||||
@@ -704,7 +704,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2022-04-26 13:29:55.087240",
|
||||
"modified": "2023-04-16 13:29:55.087240",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Selling",
|
||||
|
||||
@@ -36,8 +36,24 @@ class ItemGroup(NestedSet, WebsiteGenerator):
|
||||
|
||||
self.make_route()
|
||||
self.validate_item_group_defaults()
|
||||
self.check_item_tax()
|
||||
ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
|
||||
|
||||
def check_item_tax(self):
|
||||
"""Check whether Tax Rate is not entered twice for same Tax Type"""
|
||||
check_list = []
|
||||
for d in self.get("taxes"):
|
||||
if d.item_tax_template:
|
||||
if (d.item_tax_template, d.tax_category) in check_list:
|
||||
frappe.throw(
|
||||
_("{0} entered twice {1} in Item Taxes").format(
|
||||
frappe.bold(d.item_tax_template),
|
||||
"for tax category {0}".format(frappe.bold(d.tax_category)) if d.tax_category else "",
|
||||
)
|
||||
)
|
||||
else:
|
||||
check_list.append((d.item_tax_template, d.tax_category))
|
||||
|
||||
def on_update(self):
|
||||
NestedSet.on_update(self)
|
||||
invalidate_cache_for(self)
|
||||
|
||||
@@ -161,7 +161,7 @@ def add_standard_navbar_items():
|
||||
{
|
||||
"item_label": "User Forum",
|
||||
"item_type": "Route",
|
||||
"route": "https://discuss.erpnext.com",
|
||||
"route": "https://discuss.frappe.io",
|
||||
"is_standard": 1,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -581,6 +581,11 @@
|
||||
"title": "Bauleistungen nach § 13b UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
},
|
||||
{
|
||||
"title": "Nullsteuersatz nach § 12 Abs. 3 UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
}
|
||||
],
|
||||
"purchase_tax_templates": [
|
||||
@@ -1339,6 +1344,11 @@
|
||||
"title": "Bauleistungen nach § 13b UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
},
|
||||
{
|
||||
"title": "Nullsteuersatz nach § 12 Abs. 3 UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
}
|
||||
],
|
||||
"purchase_tax_templates": [
|
||||
@@ -2097,6 +2107,11 @@
|
||||
"title": "Bauleistungen nach § 13b UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
},
|
||||
{
|
||||
"title": "Nullsteuersatz nach § 12 Abs. 3 UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
}
|
||||
],
|
||||
"purchase_tax_templates": [
|
||||
@@ -2849,6 +2864,11 @@
|
||||
"title": "Bauleistungen nach § 13b UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
},
|
||||
{
|
||||
"title": "Nullsteuersatz nach § 12 Abs. 3 UStG",
|
||||
"is_default": 0,
|
||||
"taxes": []
|
||||
}
|
||||
],
|
||||
"purchase_tax_templates": [
|
||||
|
||||
@@ -6,6 +6,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import make_autoname, revert_series_if_last
|
||||
from frappe.query_builder.functions import CurDate, Sum, Timestamp
|
||||
from frappe.utils import cint, flt, get_link_to_form, nowtime
|
||||
from frappe.utils.data import add_days
|
||||
from frappe.utils.jinja import render_template
|
||||
@@ -176,49 +177,41 @@ def get_batch_qty(
|
||||
:param warehouse: Optional - give qty for this warehouse
|
||||
:param item_code: Optional - give qty for this item"""
|
||||
|
||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||
|
||||
out = 0
|
||||
if batch_no and warehouse:
|
||||
cond = ""
|
||||
query = (
|
||||
frappe.qb.from_(sle)
|
||||
.select(Sum(sle.actual_qty))
|
||||
.where((sle.is_cancelled == 0) & (sle.warehouse == warehouse) & (sle.batch_no == batch_no))
|
||||
)
|
||||
|
||||
if posting_date:
|
||||
if posting_time is None:
|
||||
posting_time = nowtime()
|
||||
|
||||
cond = " and timestamp(posting_date, posting_time) <= timestamp('{0}', '{1}')".format(
|
||||
posting_date, posting_time
|
||||
query = query.where(
|
||||
Timestamp(sle.posting_date, sle.posting_time) <= Timestamp(posting_date, posting_time)
|
||||
)
|
||||
|
||||
out = float(
|
||||
frappe.db.sql(
|
||||
"""select sum(actual_qty)
|
||||
from `tabStock Ledger Entry`
|
||||
where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(
|
||||
cond
|
||||
),
|
||||
(warehouse, batch_no),
|
||||
)[0][0]
|
||||
or 0
|
||||
)
|
||||
out = query.run(as_list=True)[0][0] or 0
|
||||
|
||||
if batch_no and not warehouse:
|
||||
out = frappe.db.sql(
|
||||
"""select warehouse, sum(actual_qty) as qty
|
||||
from `tabStock Ledger Entry`
|
||||
where is_cancelled = 0 and batch_no=%s
|
||||
group by warehouse""",
|
||||
batch_no,
|
||||
as_dict=1,
|
||||
)
|
||||
out = (
|
||||
frappe.qb.from_(sle)
|
||||
.select(sle.warehouse, Sum(sle.actual_qty).as_("qty"))
|
||||
.where((sle.is_cancelled == 0) & (sle.batch_no == batch_no))
|
||||
.groupby(sle.warehouse)
|
||||
).run(as_dict=True)
|
||||
|
||||
if not batch_no and item_code and warehouse:
|
||||
out = frappe.db.sql(
|
||||
"""select batch_no, sum(actual_qty) as qty
|
||||
from `tabStock Ledger Entry`
|
||||
where is_cancelled = 0 and item_code = %s and warehouse=%s
|
||||
group by batch_no""",
|
||||
(item_code, warehouse),
|
||||
as_dict=1,
|
||||
)
|
||||
out = (
|
||||
frappe.qb.from_(sle)
|
||||
.select(sle.batch_no, Sum(sle.actual_qty).as_("qty"))
|
||||
.where((sle.is_cancelled == 0) & (sle.item_code == item_code) & (sle.warehouse == warehouse))
|
||||
.groupby(sle.batch_no)
|
||||
).run(as_dict=True)
|
||||
|
||||
return out
|
||||
|
||||
@@ -314,40 +307,44 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None):
|
||||
def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
cond = ""
|
||||
batch = frappe.qb.DocType("Batch")
|
||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(batch)
|
||||
.join(sle)
|
||||
.on(batch.batch_id == sle.batch_no)
|
||||
.select(
|
||||
batch.batch_id,
|
||||
Sum(sle.actual_qty).as_("qty"),
|
||||
)
|
||||
.where(
|
||||
(sle.item_code == item_code)
|
||||
& (sle.warehouse == warehouse)
|
||||
& (sle.is_cancelled == 0)
|
||||
& ((batch.expiry_date >= CurDate()) | (batch.expiry_date.isnull()))
|
||||
)
|
||||
.groupby(batch.batch_id)
|
||||
.orderby(batch.expiry_date, batch.creation)
|
||||
)
|
||||
|
||||
if serial_no and frappe.get_cached_value("Item", item_code, "has_batch_no"):
|
||||
serial_nos = get_serial_nos(serial_no)
|
||||
batch = frappe.get_all(
|
||||
batches = frappe.get_all(
|
||||
"Serial No",
|
||||
fields=["distinct batch_no"],
|
||||
filters={"item_code": item_code, "warehouse": warehouse, "name": ("in", serial_nos)},
|
||||
)
|
||||
|
||||
if not batch:
|
||||
if not batches:
|
||||
validate_serial_no_with_batch(serial_nos, item_code)
|
||||
|
||||
if batch and len(batch) > 1:
|
||||
if batches and len(batches) > 1:
|
||||
return []
|
||||
|
||||
cond = " and `tabBatch`.name = %s" % (frappe.db.escape(batch[0].batch_no))
|
||||
query = query.where(batch.name == batches[0].batch_no)
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select batch_id, sum(`tabStock Ledger Entry`.actual_qty) as qty
|
||||
from `tabBatch`
|
||||
join `tabStock Ledger Entry` ignore index (item_code, warehouse)
|
||||
on (`tabBatch`.batch_id = `tabStock Ledger Entry`.batch_no )
|
||||
where `tabStock Ledger Entry`.item_code = %s and `tabStock Ledger Entry`.warehouse = %s
|
||||
and `tabStock Ledger Entry`.is_cancelled = 0
|
||||
and (`tabBatch`.expiry_date >= CURRENT_DATE or `tabBatch`.expiry_date IS NULL) {0}
|
||||
group by batch_id
|
||||
order by `tabBatch`.expiry_date ASC, `tabBatch`.creation ASC
|
||||
""".format(
|
||||
cond
|
||||
),
|
||||
(item_code, warehouse),
|
||||
as_dict=True,
|
||||
)
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
def validate_serial_no_with_batch(serial_nos, item_code):
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
"actual_qty",
|
||||
"installed_qty",
|
||||
"item_tax_rate",
|
||||
"column_break_atna",
|
||||
"received_qty",
|
||||
"accounting_details_section",
|
||||
"expense_account",
|
||||
"allow_zero_valuation_rate",
|
||||
@@ -832,13 +834,27 @@
|
||||
"fieldname": "material_request_item",
|
||||
"fieldtype": "Data",
|
||||
"label": "Material Request Item"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_atna",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: parent.is_internal_customer",
|
||||
"fieldname": "received_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Received Qty",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-03-20 14:24:10.406746",
|
||||
"modified": "2023-04-06 09:28:29.182053",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
||||
@@ -350,10 +350,15 @@ class Item(Document):
|
||||
check_list = []
|
||||
for d in self.get("taxes"):
|
||||
if d.item_tax_template:
|
||||
if d.item_tax_template in check_list:
|
||||
frappe.throw(_("{0} entered twice in Item Tax").format(d.item_tax_template))
|
||||
if (d.item_tax_template, d.tax_category) in check_list:
|
||||
frappe.throw(
|
||||
_("{0} entered twice {1} in Item Taxes").format(
|
||||
frappe.bold(d.item_tax_template),
|
||||
"for tax category {0}".format(frappe.bold(d.tax_category)) if d.tax_category else "",
|
||||
)
|
||||
)
|
||||
else:
|
||||
check_list.append(d.item_tax_template)
|
||||
check_list.append((d.item_tax_template, d.tax_category))
|
||||
|
||||
def validate_barcode(self):
|
||||
from stdnum import ean
|
||||
|
||||
@@ -65,6 +65,16 @@ class PurchaseReceipt(BuyingController):
|
||||
"percent_join_field": "purchase_invoice",
|
||||
"overflow_type": "receipt",
|
||||
},
|
||||
{
|
||||
"source_dt": "Purchase Receipt Item",
|
||||
"target_dt": "Delivery Note Item",
|
||||
"join_field": "delivery_note_item",
|
||||
"source_field": "received_qty",
|
||||
"target_field": "received_qty",
|
||||
"target_parent_dt": "Delivery Note",
|
||||
"target_ref_field": "qty",
|
||||
"overflow_type": "receipt",
|
||||
},
|
||||
]
|
||||
|
||||
if cint(self.is_return):
|
||||
|
||||
@@ -1544,6 +1544,72 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
res = get_item_details(args)
|
||||
self.assertEqual(res.get("last_purchase_rate"), 100)
|
||||
|
||||
def test_validate_received_qty_for_internal_pr(self):
|
||||
prepare_data_for_internal_transfer()
|
||||
customer = "_Test Internal Customer 2"
|
||||
company = "_Test Company with perpetual inventory"
|
||||
from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company)
|
||||
target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company)
|
||||
to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company)
|
||||
|
||||
# Step 1: Create Item
|
||||
item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
|
||||
|
||||
# Step 2: Create Stock Entry (Material Receipt)
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
|
||||
make_stock_entry(
|
||||
purpose="Material Receipt",
|
||||
item_code=item.name,
|
||||
qty=15,
|
||||
company=company,
|
||||
to_warehouse=from_warehouse,
|
||||
)
|
||||
|
||||
# Step 3: Create Delivery Note with Internal Customer
|
||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||
|
||||
dn = create_delivery_note(
|
||||
item_code=item.name,
|
||||
company=company,
|
||||
customer=customer,
|
||||
cost_center="Main - TCP1",
|
||||
expense_account="Cost of Goods Sold - TCP1",
|
||||
qty=10,
|
||||
rate=100,
|
||||
warehouse=from_warehouse,
|
||||
target_warehouse=target_warehouse,
|
||||
)
|
||||
|
||||
# Step 4: Create Internal Purchase Receipt
|
||||
from erpnext.controllers.status_updater import OverAllowanceError
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
|
||||
|
||||
pr = make_inter_company_purchase_receipt(dn.name)
|
||||
pr.items[0].qty = 15
|
||||
pr.items[0].from_warehouse = target_warehouse
|
||||
pr.items[0].warehouse = to_warehouse
|
||||
pr.items[0].rejected_warehouse = from_warehouse
|
||||
pr.save()
|
||||
|
||||
self.assertRaises(OverAllowanceError, pr.submit)
|
||||
|
||||
# Step 5: Test Over Receipt Allowance
|
||||
frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 50)
|
||||
|
||||
make_stock_entry(
|
||||
purpose="Material Transfer",
|
||||
item_code=item.name,
|
||||
qty=5,
|
||||
company=company,
|
||||
from_warehouse=from_warehouse,
|
||||
to_warehouse=target_warehouse,
|
||||
)
|
||||
|
||||
pr.submit()
|
||||
|
||||
frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0)
|
||||
|
||||
|
||||
def prepare_data_for_internal_transfer():
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
|
||||
|
||||
@@ -410,10 +410,10 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"search_index": 1,
|
||||
"set_only_once": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "status",
|
||||
@@ -433,7 +433,7 @@
|
||||
"icon": "fa fa-barcode",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2021-12-23 10:44:30.299450",
|
||||
"modified": "2023-04-14 15:58:46.139887",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Serial No",
|
||||
@@ -461,7 +461,6 @@
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Stock Manager",
|
||||
"set_user_permissions": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"set_posting_time",
|
||||
"inspection_required",
|
||||
"apply_putaway_rule",
|
||||
"items_tab",
|
||||
"bom_info_section",
|
||||
"from_bom",
|
||||
"use_multi_level_bom",
|
||||
@@ -256,7 +255,7 @@
|
||||
"description": "As per Stock UOM",
|
||||
"fieldname": "fg_completed_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "For Quantity",
|
||||
"label": "Finished Good Quantity ",
|
||||
"oldfieldname": "fg_completed_qty",
|
||||
"oldfieldtype": "Currency",
|
||||
"print_hide": 1
|
||||
@@ -612,11 +611,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Items"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "bom_info_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "BOM Info"
|
||||
@@ -644,8 +639,10 @@
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "section_break_7qsm",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Process Loss"
|
||||
},
|
||||
{
|
||||
"depends_on": "process_loss_percentage",
|
||||
@@ -677,7 +674,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-03 16:02:50.741816",
|
||||
"modified": "2023-04-06 12:42:56.673180",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
||||
@@ -5,7 +5,7 @@ from typing import Optional
|
||||
|
||||
import frappe
|
||||
from frappe import _, bold, msgprint
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.query_builder.functions import CombineDatetime, Sum
|
||||
from frappe.utils import cint, cstr, flt
|
||||
|
||||
import erpnext
|
||||
@@ -571,22 +571,33 @@ class StockReconciliation(StockController):
|
||||
self._cancel()
|
||||
|
||||
def recalculate_current_qty(self, item_code, batch_no):
|
||||
from erpnext.stock.stock_ledger import get_valuation_rate
|
||||
|
||||
sl_entries = []
|
||||
for row in self.items:
|
||||
if not (row.item_code == item_code and row.batch_no == batch_no):
|
||||
continue
|
||||
|
||||
row.current_qty = get_batch_qty_for_stock_reco(item_code, row.warehouse, batch_no)
|
||||
current_qty = get_batch_qty_for_stock_reco(
|
||||
item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name
|
||||
)
|
||||
|
||||
qty, val_rate = get_stock_balance(
|
||||
item_code,
|
||||
row.warehouse,
|
||||
self.posting_date,
|
||||
self.posting_time,
|
||||
with_valuation_rate=True,
|
||||
precesion = row.precision("current_qty")
|
||||
if flt(current_qty, precesion) == flt(row.current_qty, precesion):
|
||||
continue
|
||||
|
||||
val_rate = get_valuation_rate(
|
||||
item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no
|
||||
)
|
||||
|
||||
row.current_valuation_rate = val_rate
|
||||
if not row.current_qty and current_qty:
|
||||
sle = self.get_sle_for_items(row)
|
||||
sle.actual_qty = current_qty * -1
|
||||
sle.valuation_rate = val_rate
|
||||
sl_entries.append(sle)
|
||||
|
||||
row.current_qty = current_qty
|
||||
row.db_set(
|
||||
{
|
||||
"current_qty": row.current_qty,
|
||||
@@ -595,8 +606,13 @@ class StockReconciliation(StockController):
|
||||
}
|
||||
)
|
||||
|
||||
if sl_entries:
|
||||
self.make_sl_entries(sl_entries)
|
||||
|
||||
def get_batch_qty_for_stock_reco(item_code, warehouse, batch_no):
|
||||
|
||||
def get_batch_qty_for_stock_reco(
|
||||
item_code, warehouse, batch_no, posting_date, posting_time, voucher_no
|
||||
):
|
||||
ledger = frappe.qb.DocType("Stock Ledger Entry")
|
||||
|
||||
query = (
|
||||
@@ -610,6 +626,12 @@ def get_batch_qty_for_stock_reco(item_code, warehouse, batch_no):
|
||||
& (ledger.docstatus == 1)
|
||||
& (ledger.is_cancelled == 0)
|
||||
& (ledger.batch_no == batch_no)
|
||||
& (ledger.posting_date <= posting_date)
|
||||
& (
|
||||
CombineDatetime(ledger.posting_date, ledger.posting_time)
|
||||
<= CombineDatetime(posting_date, posting_time)
|
||||
)
|
||||
& (ledger.voucher_no != voucher_no)
|
||||
)
|
||||
.groupby(ledger.batch_no)
|
||||
)
|
||||
|
||||
@@ -676,6 +676,79 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
|
||||
self.assertEqual(flt(sl_entry.actual_qty), 1.0)
|
||||
self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0)
|
||||
|
||||
def test_backdated_stock_reco_entry(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
|
||||
item_code = self.make_item(
|
||||
"Test New Batch Item ABCV",
|
||||
{
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"batch_number_series": "BNS9.####",
|
||||
"create_new_batch": 1,
|
||||
},
|
||||
).name
|
||||
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
|
||||
# Added 100 Qty, Balace Qty 100
|
||||
se1 = make_stock_entry(
|
||||
item_code=item_code, posting_time="09:00:00", target=warehouse, qty=100, basic_rate=700
|
||||
)
|
||||
|
||||
# Removed 50 Qty, Balace Qty 50
|
||||
se2 = make_stock_entry(
|
||||
item_code=item_code,
|
||||
batch_no=se1.items[0].batch_no,
|
||||
posting_time="10:00:00",
|
||||
source=warehouse,
|
||||
qty=50,
|
||||
basic_rate=700,
|
||||
)
|
||||
|
||||
# Stock Reco for 100, Balace Qty 100
|
||||
stock_reco = create_stock_reconciliation(
|
||||
item_code=item_code,
|
||||
posting_time="11:00:00",
|
||||
warehouse=warehouse,
|
||||
batch_no=se1.items[0].batch_no,
|
||||
qty=100,
|
||||
rate=100,
|
||||
)
|
||||
|
||||
# Removed 50 Qty, Balace Qty 50
|
||||
make_stock_entry(
|
||||
item_code=item_code,
|
||||
batch_no=se1.items[0].batch_no,
|
||||
posting_time="12:00:00",
|
||||
source=warehouse,
|
||||
qty=50,
|
||||
basic_rate=700,
|
||||
)
|
||||
|
||||
self.assertFalse(frappe.db.exists("Repost Item Valuation", {"voucher_no": stock_reco.name}))
|
||||
|
||||
# Cancel the backdated Stock Entry se2,
|
||||
# Since Stock Reco entry in the future the Balace Qty should remain as it's (50)
|
||||
|
||||
se2.cancel()
|
||||
|
||||
self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": stock_reco.name}))
|
||||
|
||||
self.assertEqual(
|
||||
frappe.db.get_value("Repost Item Valuation", {"voucher_no": stock_reco.name}, "status"),
|
||||
"Completed",
|
||||
)
|
||||
|
||||
sle = frappe.get_all(
|
||||
"Stock Ledger Entry",
|
||||
filters={"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0},
|
||||
fields=["qty_after_transaction"],
|
||||
order_by="posting_time desc, creation desc",
|
||||
)
|
||||
|
||||
self.assertEqual(flt(sle[0].qty_after_transaction), flt(50.0))
|
||||
|
||||
|
||||
def create_batch_item_with_batch(item_name, batch_id):
|
||||
batch_item_doc = create_item(item_name, is_stock_item=1)
|
||||
|
||||
@@ -35,14 +35,7 @@ purchase_doctypes = [
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_details(
|
||||
args,
|
||||
doc=None,
|
||||
for_validate=False,
|
||||
overwrite_warehouse=True,
|
||||
return_basic_details=False,
|
||||
basic_details=None,
|
||||
):
|
||||
def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=True):
|
||||
"""
|
||||
args = {
|
||||
"item_code": "",
|
||||
@@ -80,12 +73,7 @@ def get_item_details(
|
||||
if doc.get("doctype") == "Purchase Invoice":
|
||||
args["bill_date"] = doc.get("bill_date")
|
||||
|
||||
if not basic_details:
|
||||
out = get_basic_details(args, item, overwrite_warehouse)
|
||||
else:
|
||||
out = basic_details
|
||||
|
||||
basic_details = out.copy()
|
||||
out = get_basic_details(args, item, overwrite_warehouse)
|
||||
|
||||
get_item_tax_template(args, item, out)
|
||||
out["item_tax_rate"] = get_item_tax_map(
|
||||
@@ -154,11 +142,7 @@ def get_item_details(
|
||||
out.amount = flt(args.qty) * flt(out.rate)
|
||||
|
||||
out = remove_standard_fields(out)
|
||||
|
||||
if return_basic_details:
|
||||
return out, basic_details
|
||||
else:
|
||||
return out
|
||||
return out
|
||||
|
||||
|
||||
def remove_standard_fields(details):
|
||||
@@ -637,7 +621,9 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
|
||||
taxes_with_no_validity.append(tax)
|
||||
|
||||
if taxes_with_validity:
|
||||
taxes = sorted(taxes_with_validity, key=lambda i: i.valid_from, reverse=True)
|
||||
taxes = sorted(
|
||||
taxes_with_validity, key=lambda i: i.valid_from or tax.maximum_net_rate, reverse=True
|
||||
)
|
||||
else:
|
||||
taxes = taxes_with_no_validity
|
||||
|
||||
|
||||
@@ -11,6 +11,13 @@ frappe.query_reports["Warehouse Wise Stock Balance"] = {
|
||||
"options": "Company",
|
||||
"reqd": 1,
|
||||
"default": frappe.defaults.get_user_default("Company")
|
||||
},
|
||||
{
|
||||
"fieldname":"show_disabled_warehouses",
|
||||
"label": __("Show Disabled Warehouses"),
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
|
||||
}
|
||||
],
|
||||
"initial_depth": 3,
|
||||
|
||||
@@ -11,6 +11,7 @@ from frappe.query_builder.functions import Sum
|
||||
class StockBalanceFilter(TypedDict):
|
||||
company: Optional[str]
|
||||
warehouse: Optional[str]
|
||||
show_disabled_warehouses: Optional[int]
|
||||
|
||||
|
||||
SLEntry = Dict[str, Any]
|
||||
@@ -18,7 +19,7 @@ SLEntry = Dict[str, Any]
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
columns = get_columns()
|
||||
columns = get_columns(filters)
|
||||
data = get_data(filters)
|
||||
|
||||
return columns, data
|
||||
@@ -42,10 +43,14 @@ def get_warehouse_wise_balance(filters: StockBalanceFilter) -> List[SLEntry]:
|
||||
|
||||
|
||||
def get_warehouses(report_filters: StockBalanceFilter):
|
||||
filters = {"company": report_filters.company, "disabled": 0}
|
||||
if report_filters.get("show_disabled_warehouses"):
|
||||
filters["disabled"] = ("in", [0, report_filters.show_disabled_warehouses])
|
||||
|
||||
return frappe.get_all(
|
||||
"Warehouse",
|
||||
fields=["name", "parent_warehouse", "is_group"],
|
||||
filters={"company": report_filters.company},
|
||||
fields=["name", "parent_warehouse", "is_group", "disabled"],
|
||||
filters=filters,
|
||||
order_by="lft",
|
||||
)
|
||||
|
||||
@@ -90,8 +95,8 @@ def set_balance_in_parent(warehouses):
|
||||
update_balance(warehouse, warehouse.stock_balance)
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
def get_columns(filters: StockBalanceFilter) -> List[Dict]:
|
||||
columns = [
|
||||
{
|
||||
"label": _("Warehouse"),
|
||||
"fieldname": "name",
|
||||
@@ -101,3 +106,15 @@ def get_columns():
|
||||
},
|
||||
{"label": _("Stock Balance"), "fieldname": "stock_balance", "fieldtype": "Float", "width": 150},
|
||||
]
|
||||
|
||||
if filters.get("show_disabled_warehouses"):
|
||||
columns.append(
|
||||
{
|
||||
"label": _("Warehouse Disabled?"),
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"width": 200,
|
||||
}
|
||||
)
|
||||
|
||||
return columns
|
||||
|
||||
@@ -544,6 +544,14 @@ class update_entries_after(object):
|
||||
if not self.args.get("sle_id"):
|
||||
self.get_dynamic_incoming_outgoing_rate(sle)
|
||||
|
||||
if (
|
||||
sle.voucher_type == "Stock Reconciliation"
|
||||
and sle.batch_no
|
||||
and sle.voucher_detail_no
|
||||
and sle.actual_qty < 0
|
||||
):
|
||||
self.reset_actual_qty_for_stock_reco(sle)
|
||||
|
||||
if (
|
||||
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
|
||||
and sle.voucher_detail_no
|
||||
@@ -605,6 +613,16 @@ class update_entries_after(object):
|
||||
if not self.args.get("sle_id"):
|
||||
self.update_outgoing_rate_on_transaction(sle)
|
||||
|
||||
def reset_actual_qty_for_stock_reco(self, sle):
|
||||
current_qty = frappe.get_cached_value(
|
||||
"Stock Reconciliation Item", sle.voucher_detail_no, "current_qty"
|
||||
)
|
||||
|
||||
if current_qty:
|
||||
sle.actual_qty = current_qty * -1
|
||||
elif current_qty == 0:
|
||||
sle.is_cancelled = 1
|
||||
|
||||
def validate_negative_stock(self, sle):
|
||||
"""
|
||||
validate negative stock for entries current datetime onwards
|
||||
@@ -1369,12 +1387,8 @@ def update_qty_in_future_sle(args, allow_negative_stock=False):
|
||||
|
||||
def regenerate_sle_for_batch_stock_reco(detail):
|
||||
doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no)
|
||||
doc.docstatus = 2
|
||||
doc.update_stock_ledger()
|
||||
|
||||
doc.recalculate_current_qty(detail.item_code, detail.batch_no)
|
||||
doc.docstatus = 1
|
||||
doc.update_stock_ledger()
|
||||
doc.repost_future_sle_and_gle()
|
||||
|
||||
|
||||
def get_stock_reco_qty_shift(args):
|
||||
@@ -1400,34 +1414,53 @@ def get_stock_reco_qty_shift(args):
|
||||
return stock_reco_qty_shift
|
||||
|
||||
|
||||
def get_next_stock_reco(args):
|
||||
def get_next_stock_reco(kwargs):
|
||||
"""Returns next nearest stock reconciliaton's details."""
|
||||
|
||||
return frappe.db.sql(
|
||||
"""
|
||||
select
|
||||
name, posting_date, posting_time, creation, voucher_no, item_code, batch_no, actual_qty
|
||||
from
|
||||
`tabStock Ledger Entry`
|
||||
where
|
||||
item_code = %(item_code)s
|
||||
and warehouse = %(warehouse)s
|
||||
and voucher_type = 'Stock Reconciliation'
|
||||
and voucher_no != %(voucher_no)s
|
||||
and is_cancelled = 0
|
||||
and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
|
||||
or (
|
||||
timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
|
||||
and creation > %(creation)s
|
||||
sle = frappe.qb.DocType("Stock Ledger Entry")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(sle)
|
||||
.select(
|
||||
sle.name,
|
||||
sle.posting_date,
|
||||
sle.posting_time,
|
||||
sle.creation,
|
||||
sle.voucher_no,
|
||||
sle.item_code,
|
||||
sle.batch_no,
|
||||
sle.actual_qty,
|
||||
)
|
||||
.where(
|
||||
(sle.item_code == kwargs.get("item_code"))
|
||||
& (sle.warehouse == kwargs.get("warehouse"))
|
||||
& (sle.voucher_type == "Stock Reconciliation")
|
||||
& (sle.voucher_no != kwargs.get("voucher_no"))
|
||||
& (sle.is_cancelled == 0)
|
||||
& (
|
||||
(
|
||||
CombineDatetime(sle.posting_date, sle.posting_time)
|
||||
> CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
|
||||
| (
|
||||
(
|
||||
CombineDatetime(sle.posting_date, sle.posting_time)
|
||||
== CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
|
||||
)
|
||||
& (sle.creation > kwargs.get("creation"))
|
||||
)
|
||||
)
|
||||
)
|
||||
order by timestamp(posting_date, posting_time) asc, creation asc
|
||||
limit 1
|
||||
""",
|
||||
args,
|
||||
as_dict=1,
|
||||
)
|
||||
.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
|
||||
.orderby(sle.creation)
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
if kwargs.get("batch_no"):
|
||||
query.where(sle.batch_no == kwargs.get("batch_no"))
|
||||
|
||||
return query.run(as_dict=True)
|
||||
|
||||
|
||||
def get_datetime_limit_condition(detail):
|
||||
return f"""
|
||||
|
||||
@@ -245,17 +245,17 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
item.expense_account = expense_account
|
||||
|
||||
def update_status(self, status=None, update_modified=False):
|
||||
if self.docstatus >= 1 and not status:
|
||||
if self.docstatus == 1:
|
||||
if not status:
|
||||
if self.docstatus == 0:
|
||||
status = "Draft"
|
||||
elif self.docstatus == 1:
|
||||
status = "Completed"
|
||||
if self.is_return:
|
||||
status = "Return"
|
||||
return_against = frappe.get_doc("Subcontracting Receipt", self.return_against)
|
||||
return_against.run_method("update_status")
|
||||
else:
|
||||
if self.per_returned == 100:
|
||||
status = "Return Issued"
|
||||
elif self.status == "Draft":
|
||||
status = "Completed"
|
||||
elif self.per_returned == 100:
|
||||
status = "Return Issued"
|
||||
elif self.docstatus == 2:
|
||||
status = "Cancelled"
|
||||
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
|
||||
<div class="product-price">
|
||||
<!-- Final Price -->
|
||||
{{ price_info.formatted_price_sales_uom }}
|
||||
<span itemprop="offers" itemscope itemtype="https://schema.org/Offer">
|
||||
<span itemprop="price" content="{{ price_info.price_list_rate }}">{{ price_info.formatted_price_sales_uom }}</span>
|
||||
<span style="display:none;" itemprop="priceCurrency" content="{{ price_info.currency }}">{{ price_info.currency }}</span>
|
||||
</span>
|
||||
|
||||
<!-- Striked Price and Discount -->
|
||||
{% if price_info.formatted_mrp %}
|
||||
|
||||
@@ -1875,6 +1875,7 @@ Parents Teacher Meeting Attendance,Eltern Lehrer Treffen Teilnahme,
|
||||
Part-time,Teilzeit,
|
||||
Partially Depreciated,Teilweise abgeschrieben,
|
||||
Partially Received,Teilweise erhalten,
|
||||
Partly Paid,Teilweise bezahlt,
|
||||
Party,Partei,
|
||||
Party Name,Name der Partei,
|
||||
Party Type,Partei-Typ,
|
||||
|
||||
|
Can't render this file because it is too large.
|
Reference in New Issue
Block a user