diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 35dc14d153b2..0abe05b59151 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -4,7 +4,7 @@
import frappe
from frappe.utils.user import is_website_user
-__version__ = "15.47.1"
+__version__ = "15.48.0"
def get_default_company(user=None):
"""Get default company for user"""
diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js
index 49d5396c0f37..a61fcb4f5301 100644
--- a/erpnext/accounts/doctype/account/account_tree.js
+++ b/erpnext/accounts/doctype/account/account_tree.js
@@ -237,19 +237,22 @@ frappe.treeview_settings["Account"] = {
},
post_render: function (treeview) {
frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree;
- treeview.page.set_primary_action(
- __("New"),
- function () {
- let root_company = treeview.page.fields_dict.root_company.get_value();
-
- if (root_company) {
- frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]);
- } else {
- treeview.new_node();
- }
- },
- "add"
- );
+ if (treeview.can_create) {
+ treeview.page.set_primary_action(
+ __("New"),
+ function () {
+ let root_company = treeview.page.fields_dict.root_company.get_value();
+ if (root_company) {
+ frappe.throw(__("Please add the account to root level Company - {0}"), [
+ root_company,
+ ]);
+ } else {
+ treeview.new_node();
+ }
+ },
+ "add"
+ );
+ }
},
toolbar: [
{
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
index b15745d834c3..7a653e12c727 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -120,6 +120,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
args: {
bank_account: frm.doc.bank_account,
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
+ company: frm.doc.company,
},
callback: (response) => {
frm.set_value("account_opening_balance", response.message);
@@ -135,6 +136,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
args: {
bank_account: frm.doc.bank_account,
till_date: frm.doc.bank_statement_to_date,
+ company: frm.doc.company,
},
callback: (response) => {
frm.cleared_balance = response.message;
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index 1f4487d3527a..f0d99c8f515d 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -78,10 +78,17 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
@frappe.whitelist()
-def get_account_balance(bank_account, till_date):
+def get_account_balance(bank_account, till_date, company):
# returns account balance till the specified date
account = frappe.db.get_value("Bank Account", bank_account, "account")
- filters = frappe._dict({"account": account, "report_date": till_date, "include_pos_transactions": 1})
+ filters = frappe._dict(
+ {
+ "account": account,
+ "report_date": till_date,
+ "include_pos_transactions": 1,
+ "company": company,
+ }
+ )
data = get_entries(filters)
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
@@ -93,11 +100,7 @@ def get_account_balance(bank_account, till_date):
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
- bank_bal = (
- flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
- )
-
- return bank_bal
+ return flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
@frappe.whitelist()
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/__init__.py b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.js b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.js
new file mode 100644
index 000000000000..f2506ae760ae
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.js
@@ -0,0 +1,388 @@
+// Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide("erpnext.accounts.bank_reconciliation");
+
+frappe.ui.form.on("Bank Reconciliation Tool ERPNext", {
+ refresh(frm) {
+ frm.disable_save();
+ // frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool"));
+ if (frm.doc.closing_balance_as_per_bank_statement && frm.doc.closing_balance_as_per_erp) {
+ frm.set_value(
+ "difference_amount",
+ (frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp)
+ );
+ }
+
+ frm.add_custom_button(__("Upload Bank Statement"), () =>
+ frappe.call({
+ method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
+ args: {
+ dt: frm.doc.doctype,
+ dn: frm.doc.name,
+ company: frm.doc.company,
+ bank_account: frm.doc.bank_account,
+ },
+ callback: function (r) {
+ if (!r.exc) {
+ var doc = frappe.model.sync(r.message);
+ frappe.set_route("Form", doc[0].doctype, doc[0].name);
+ }
+ },
+ })
+ );
+ if (frm.doc.closing_balance_as_per_erp && frm.doc.closing_balance_as_per_bank_statement) {
+ frm.set_value(
+ "difference_amount",
+ frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp
+ );
+ }
+ // console.log(" NOT ALLLOCSATE",frm.doc.bank_statement.length)
+ if (frm.doc.bank_statement.length && frm.doc.erp_transaction.length) {
+ // console.log("ALLLOCSATE")
+ frm.add_custom_button(__("Allocate"), () => frm.trigger("allocate"));
+ frm.change_custom_button_type(__("Allocate"), null, "primary");
+ frm.change_custom_button_type(__("Get Unreconciled Entries"), null, "default");
+ }
+ if (frm.doc.matching_table.length) {
+ frm.add_custom_button(__("Reconcile"), () => frm.trigger("reconcile"));
+ frm.change_custom_button_type(__("Reconcile"), null, "primary");
+ frm.change_custom_button_type(__("Get Unreconciled Entries"), null, "default");
+ frm.change_custom_button_type(__("Allocate"), null, "default");
+ frm.doc.matching_table.map((i) => {
+ frm.doc.erp_transaction.map((j) => {
+ if (i.reference_id == j.reference_id) {
+ frm.doc.bank_statement.map((k) => {
+ if (k.bank_transaction_id == i.bank_transaction_id) {
+ if ((j.withdraw || j.deposit) - (k.withdraw || k.deposit) >= 0) {
+ j.remaining_amount =
+ (j.withdraw || j.deposit) - (k.withdraw || k.deposit);
+ } else {
+ j.remaining_amount = 0;
+ }
+ }
+ });
+ }
+ });
+ });
+ frm.refresh_field("erp_transaction");
+ }
+
+ },
+ setup: function (frm) {
+ frm.set_query("bank_account", function () {
+ return {
+ filters: {
+ company: frm.doc.company,
+ is_company_account: 1,
+ },
+ };
+ });
+ // let no_bank_transactions_text = `
${__(
+ // "No Matching Bank Transactions Found"
+ // )}
`;
+ // set_field_options("no_bank_transactions", no_bank_transactions_text);
+ },
+ bank_account: function (frm) {
+ frappe.db.get_value("Bank Account", frm.doc.bank_account, "account", (r) => {
+ frappe.db.get_value("Account", r.account, "account_currency", (r) => {
+ frm.doc.account_currency = r.account_currency;
+ // console.log('rrrr',r, frm.doc.account_currency)
+ // frm.trigger("render_chart");
+ });
+ });
+ // frm.trigger("render_chart");
+ frm.trigger("get_account_opening_balance");
+ frm.doc.difference_amount = (frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp)
+ frm.refresh_fields();
+ frm.add_custom_button(__("Get Unreconciled Entries"), function () {
+ frm.set_value(
+ "difference_amount",
+ (frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp)
+ );
+ frappe.call({
+ method: "erpnext.accounts.doctype.bank_reconciliation_tool_erpnext.bank_reconciliation_tool_erpnext.get_bank_transaction",
+ args: {
+ bank_account: frm.doc.bank_account,
+ company: frm.doc.company,
+ from_statement_date: frm.doc.from_statement_date,
+ to_statement_date: frm.doc.to_statement_date,
+ },
+ callback: (response) => {
+ let existingTransactionIds = new Set(
+ frm.doc.bank_statement.map((row) => row.bank_transaction_id) // Collect existing IDs
+ );
+ // console.log('Response:', response.message);
+
+ if (Array.isArray(response.message)) {
+ response.message.forEach((transaction) => {
+ if (!existingTransactionIds.has(transaction.name)) {
+ if (transaction.deposit || transaction.withdrawal) {
+ // Add a new child row to the bank_statement table
+ let bankTransaction = frm.add_child("bank_statement");
+ bankTransaction.date = transaction.date;
+ bankTransaction.bank_transaction_id = transaction.name;
+ bankTransaction.description = transaction.description;
+ bankTransaction.deposit = transaction.deposit;
+ bankTransaction.withdraw = transaction.withdrawal;
+ bankTransaction.reference_no = transaction.reference_number;
+ bankTransaction.unallocated_amount = transaction.unallocated_amount;
+
+ // Track added transaction IDs
+ existingTransactionIds.add(transaction.name);
+ }
+ }
+ });
+ } else {
+ console.error("Invalid response.message:", response.message);
+ }
+
+ frm.refresh_field("bank_statement");
+ },
+ });
+ frappe.call({
+ method: "erpnext.accounts.doctype.bank_reconciliation_tool_erpnext.bank_reconciliation_tool_erpnext.get_erp_transaction",
+ args: {
+ bank_account: frm.doc.bank_account,
+ company: frm.doc.company,
+ from_statement_date: frm.doc.from_erp_date,
+ to_statement_date: frm.doc.to_erp_date,
+ },
+ callback: async (response) => {
+ // Create a Set of existing reference IDs in the `erp_transaction` table
+ let existingReferenceIds = new Set(
+ frm.doc.erp_transaction.map((row) => row.reference_id)
+ );
+
+ for (const i of response.message) {
+ if (i.paid_amount > 0 && !existingReferenceIds.has(i.name)) {
+ // Add the transaction if it does not already exist
+ let bnk_tr = frm.add_child("erp_transaction");
+ bnk_tr.date = i.posting_date;
+ bnk_tr.reference_id = i.name; // Unique reference ID
+ bnk_tr.reference_number = i.reference_no;
+ bnk_tr.reference_doc = i.doctype;
+
+ if (i.doctype === "Payment Entry") {
+ const payment_type = await frappe.db.get_value(
+ "Payment Entry",
+ i.name,
+ "payment_type"
+ );
+
+ if (payment_type.message.payment_type === "Pay") {
+ bnk_tr.withdraw = i.paid_amount;
+ bnk_tr.deposit = 0;
+ } else if (payment_type.message.payment_type === "Receive") {
+ bnk_tr.deposit = i.paid_amount;
+ bnk_tr.withdraw = 0;
+ } else {
+ bnk_tr.withdraw = i.paid_amount;
+ bnk_tr.deposit = 0;
+ }
+ } else if (i.doctype === "Journal Entry") {
+ if (i.bank == 'Credit'){
+ bnk_tr.deposit = 0;
+ bnk_tr.withdraw = i.paid_amount; // Confirm logic here.
+ }
+ else if (i.bank == 'Debit'){
+ bnk_tr.deposit = i.paid_amount;
+ bnk_tr.withdraw = 0; // Confirm logic here.
+ }
+ }
+
+ // Add the reference ID to the Set to track it
+ existingReferenceIds.add(i.name);
+ }
+ }
+
+ // Refresh field after processing all transactions
+ frm.refresh_field("erp_transaction");
+ // Add custom button for Allocate
+ frm.add_custom_button(__("Allocate"), () => frm.trigger("allocate"));
+ frm.change_custom_button_type(__("Allocate"), null, "primary");
+ frm.change_custom_button_type(__("Get Unreconciled Entries"), null, "default");
+ },
+ });
+ });
+ },
+ get_account_opening_balance(frm) {
+ if (frm.doc.bank_account && frm.doc.from_date && frm.doc.to_date) {
+ frappe.call({
+ method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+ args: {
+ bank_account: frm.doc.bank_account,
+ till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1),
+ company: frm.doc.company
+ },
+ callback: (response) => {
+ frm.set_value("opening_balance", response.message);
+ },
+ });
+ }
+ },
+
+ onload: function (frm) {
+ // Set default filter dates
+ let today = frappe.datetime.get_today();
+ frm.doc.from_date = frappe.datetime.add_months(today, -1);
+ frm.doc.to_date = today;
+ frm.doc.from_statement_date = frappe.datetime.add_months(today, -1);
+ frm.doc.to_statement_date = today;
+ frm.doc.from_erp_date = frappe.datetime.add_months(today, -1);
+ frm.doc.to_erp_date = today;
+ frm.trigger("bank_account");
+ },
+
+ opening_balance(frm) {
+ frm.set_value("closing_balance_as_per_erp", frm.doc.opening_balance);
+ // console.log("CLOSING BAL")
+ frappe.call({
+ method: "erpnext.accounts.doctype.bank_reconciliation_tool_erpnext.bank_reconciliation_tool_erpnext.get_closing_bal",
+ args: {
+ opening_bal: frm.doc.opening_balance,
+ from_date: frm.doc.from_date,
+ to_date: frm.doc.to_date,
+ bank_account: frm.doc.bank_account,
+ },
+ callback: function (r) {
+ // console.log('GHJJHJHJHJBBJ', r)
+ if (!r.exc) {
+ if (r.message) {
+ frm.doc.closing_balance_as_per_bank_statement = r.message;
+ }
+ }
+ frm.refresh_field("closing_balance_as_per_bank_statement");
+ },
+ });
+ },
+
+ closing_balance_as_per_erp(frm) {
+ if (frm.doc.closing_balance_as_per_bank_statement) {
+ frm.set_value(
+ "difference_amount",
+ (frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp)
+ );
+ }
+ },
+ closing_balance_as_per_bank_statement(frm) {
+ if (frm.doc.closing_balance_as_per_bank_statement) {
+ frm.set_value(
+ "difference_amount",
+ (frm.doc.closing_balance_as_per_bank_statement - frm.doc.closing_balance_as_per_erp)
+ );
+ }
+ },
+ // make_reconciliation_tool(frm) {
+ // frm.get_field("reconciliation_tool_cards").$wrapper.empty();
+ // if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+ // frm.trigger("get_cleared_balance").then(() => {
+ // if (
+ // frm.doc.bank_account &&
+ // frm.doc.from_date &&
+ // frm.doc.to_date
+ // ) {
+ // frm.trigger("render_chart");
+ // frm.trigger("render");
+ // frappe.utils.scroll_to(frm.get_field("reconciliation_tool_cards").$wrapper, true, 30);
+ // }
+ // });
+ // }
+ // },
+ // render_chart(frm) {
+ // frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager({
+ // $reconciliation_tool_cards: frm.get_field("reconciliation_tool_cards").$wrapper,
+ // bank_statement_closing_balance: frm.doc.closing_balance_as_per_bank_statement,
+ // cleared_balance: frm.doc.difference_amount,
+ // currency: frm.doc.account_currency,
+ // });
+ // },
+ allocate(frm) {
+ let bank_statement = frm.fields_dict.bank_statement.grid.get_selected_children();
+ if (!bank_statement.length) {
+ bank_statement = frm.doc.bank_statement;
+ }
+ let erp_transaction = frm.fields_dict.erp_transaction.grid.get_selected_children();
+ if (!erp_transaction.length) {
+ erp_transaction = frm.doc.erp_transaction;
+ }
+ return frm.call({
+ doc: frm.doc,
+ method: "allocate_entries",
+ args: {
+ bank_statement: bank_statement,
+ erp_transaction: erp_transaction,
+ },
+ callback: () => {
+ frm.refresh();
+ },
+ });
+ },
+
+ reconcile(frm) {
+ frm.doc.matching_table.map((i) => {
+ frm.call({
+ method: "erpnext.accounts.doctype.bank_reconciliation_tool_erpnext.bank_reconciliation_tool_erpnext.reconcile_bnk_transaction",
+ args: {
+ bank_transaction_id: i.bank_transaction_id,
+ amount: i.matched_amount,
+ name: i.reference_id,
+ payment_document: i.reference_to,
+ },
+ callback: function (r) {
+ frm.clear_table("matching_table");
+ frm.refresh();
+ // console.log('GHJJHJHJHJBBJ')
+ if (!r.exc) {
+ if (r.message) {
+ frappe.msgprint("done");
+ }
+ }
+ },
+ });
+ });
+ },
+
+ // render_chart(frm) {
+ // frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager({
+ // $reconciliation_tool_cards: frm.get_field("reconciliation_tool_cards").$wrapper,
+ // bank_statement_closing_balance: frm.doc.closing_balance_as_per_bank_statement,
+ // cleared_balance: frm.cleared_balance,
+ // currency: frm.doc.account_currency,
+ // });
+ // },
+
+ // render(frm) {
+ // if (frm.doc.bank_account) {
+ // frm.bank_reconciliation_data_table_manager =
+ // new erpnext.accounts.bank_reconciliation.DataTableManager({
+ // company: frm.doc.company,
+ // bank_account: frm.doc.bank_account,
+ // $reconciliation_tool_dt: frm.get_field("reconciliation_tool_dt").$wrapper,
+ // $no_bank_transactions: frm.get_field("no_bank_transactions").$wrapper,
+ // bank_statement_from_date: frm.doc.from_date,
+ // bank_statement_to_date: frm.doc.to_date,
+ // filter_by_reference_date: frm.doc.from_statement_date,
+ // from_reference_date: frm.doc.from_statement_date,
+ // to_reference_date: frm.doc.to_statement_date,
+ // bank_statement_closing_balance: frm.doc.closing_balance_as_per_bank_statement,
+ // cards_manager: frm.cards_manager,
+ // });
+ // }
+ // },
+
+ // get_cleared_balance(frm) {
+ // if (frm.doc.bank_account && frm.doc.bank_statement_to_date) {
+ // return frappe.call({
+ // method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
+ // args: {
+ // bank_account: frm.doc.bank_account,
+ // till_date: frm.doc.bank_statement_to_date,
+ // },
+ // callback: (response) => {
+ // frm.cleared_balance = response.message;
+ // },
+ // });
+ // }
+ // },
+});
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.json b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.json
new file mode 100644
index 000000000000..73e70cb8b7a5
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.json
@@ -0,0 +1,196 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-01-08 14:08:28.332086",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "bank_account",
+ "from_date",
+ "opening_balance",
+ "column_break_modz",
+ "closing_balance_as_per_bank_statement",
+ "closing_balance_as_per_erp",
+ "to_date",
+ "difference_amount",
+ "filters_section",
+ "from_statement_date",
+ "from_erp_date",
+ "column_break_mmtl",
+ "to_statement_date",
+ "to_erp_date",
+ "unreconciled__entries_section",
+ "bank_statement",
+ "column_break_krek",
+ "erp_transaction",
+ "allocated_entries_section",
+ "matching_table",
+ "reconcile_section",
+ "reconciliation_tool_cards",
+ "reconciliation_tool_dt",
+ "no_bank_transactions"
+ ],
+ "fields": [
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company"
+ },
+ {
+ "fieldname": "bank_account",
+ "fieldtype": "Link",
+ "label": "Bank Account",
+ "options": "Bank Account"
+ },
+ {
+ "depends_on": "eval: doc.bank_account",
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "label": "From Date"
+ },
+ {
+ "depends_on": "eval:doc.bank_account",
+ "fieldname": "opening_balance",
+ "fieldtype": "Currency",
+ "label": "Opening Balance",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_modz",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:doc.bank_account",
+ "fieldname": "closing_balance_as_per_bank_statement",
+ "fieldtype": "Currency",
+ "label": "Closing Balance as per Bank Statement"
+ },
+ {
+ "depends_on": "eval:doc.bank_account",
+ "fieldname": "closing_balance_as_per_erp",
+ "fieldtype": "Currency",
+ "label": "Closing Balance as per ERP"
+ },
+ {
+ "depends_on": "eval: doc.bank_account",
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "label": "To Date "
+ },
+ {
+ "depends_on": "eval:doc.bank_account",
+ "fieldname": "difference_amount",
+ "fieldtype": "Currency",
+ "label": "Difference Amount",
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "depends_on": "eval:doc.bank_account",
+ "fieldname": "filters_section",
+ "fieldtype": "Section Break",
+ "label": "Filters"
+ },
+ {
+ "fieldname": "from_statement_date",
+ "fieldtype": "Date",
+ "label": "From Statement Date "
+ },
+ {
+ "fieldname": "from_erp_date",
+ "fieldtype": "Date",
+ "label": "From ERP Date"
+ },
+ {
+ "fieldname": "column_break_mmtl",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "to_statement_date",
+ "fieldtype": "Date",
+ "label": "To Statement Date"
+ },
+ {
+ "fieldname": "to_erp_date",
+ "fieldtype": "Date",
+ "label": "To ERP Date"
+ },
+ {
+ "depends_on": "eval:(doc.bank_statement).length || (doc.erp_transaction).length",
+ "fieldname": "unreconciled__entries_section",
+ "fieldtype": "Section Break",
+ "label": "Unreconciled entries"
+ },
+ {
+ "fieldname": "bank_statement",
+ "fieldtype": "Table",
+ "label": "Bank Statement",
+ "options": "Bank Statement"
+ },
+ {
+ "fieldname": "column_break_krek",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "erp_transaction",
+ "fieldtype": "Table",
+ "label": "ERP Transaction",
+ "options": "ERP Transaction"
+ },
+ {
+ "depends_on": "matching_table",
+ "fieldname": "allocated_entries_section",
+ "fieldtype": "Section Break",
+ "label": "Allocated Entries"
+ },
+ {
+ "fieldname": "matching_table",
+ "fieldtype": "Table",
+ "label": "Matching Table",
+ "options": "Matching Table"
+ },
+ {
+ "fieldname": "reconcile_section",
+ "fieldtype": "Section Break",
+ "label": "Reconcile"
+ },
+ {
+ "fieldname": "reconciliation_tool_cards",
+ "fieldtype": "HTML"
+ },
+ {
+ "fieldname": "reconciliation_tool_dt",
+ "fieldtype": "HTML"
+ },
+ {
+ "fieldname": "no_bank_transactions",
+ "fieldtype": "HTML"
+ }
+ ],
+ "hide_toolbar": 1,
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2025-01-13 15:20:07.590550",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Reconciliation Tool ERPNext",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "System Manager",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.py b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.py
new file mode 100644
index 000000000000..eaa5d9605706
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/bank_reconciliation_tool_erpnext.py
@@ -0,0 +1,229 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+from frappe.utils import flt
+
+from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import get_linked_payments
+
+
+class BankReconciliationToolERPNext(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from erpnext.accounts.doctype.bank_statement.bank_statement import BankStatement
+ from erpnext.accounts.doctype.erp_transaction.erp_transaction import ERPTransaction
+ from erpnext.accounts.doctype.matching_table.matching_table import MatchingTable
+ from frappe.types import DF
+
+ bank_account: DF.Link | None
+ bank_statement: DF.Table[BankStatement]
+ closing_balance_as_per_bank_statement: DF.Currency
+ closing_balance_as_per_erp: DF.Currency
+ company: DF.Link | None
+ difference_amount: DF.Currency
+ erp_transaction: DF.Table[ERPTransaction]
+ from_date: DF.Date | None
+ from_erp_date: DF.Date | None
+ from_statement_date: DF.Date | None
+ matching_table: DF.Table[MatchingTable]
+ opening_balance: DF.Currency
+ to_date: DF.Date | None
+ to_erp_date: DF.Date | None
+ to_statement_date: DF.Date | None
+ # end: auto-generated types
+ # pass
+ def validate_entries(self):
+ if not self.get("erp_transaction"):
+ frappe.throw(_("No records found in the ERP Transactions table"))
+
+ if not self.get("bank_statement"):
+ frappe.throw(_("No records found in the Bank Statement table"))
+
+ def get_allocated_entry(self, pay, bnk_st, allocated_amount):
+ res = frappe._dict(
+ {
+ "bank_transaction_id": bnk_st.get("bank_transaction_id"),
+ "reference_to": pay.get("reference_doc"),
+ "matched_amount": allocated_amount,
+ "reference_id": pay.get("reference_id"),
+ }
+ )
+
+ return res
+
+ @frappe.whitelist()
+ def allocate_entries(self, args):
+ self.validate_entries()
+
+ entries = []
+
+ for pay in args.get("erp_transaction"):
+ # Initialize unreconciled_amount for deposit/withdraw
+ if flt(pay.get("deposit")) > 0 and flt(pay.get("withdraw")) == 0:
+ pay["unreconciled_amount"] = pay["deposit"]
+ elif flt(pay.get("withdraw")) > 0 and flt(pay.get("deposit")) == 0:
+ pay["unreconciled_amount"] = pay["withdraw"]
+
+ for bnk_st in args.get("bank_statement"):
+ allocated_amount = min(
+ pay.get("deposit", 0) or pay.get("withdraw", 0), bnk_st["unallocated_amount"]
+ )
+
+ res = self.get_allocated_entry(pay, bnk_st, allocated_amount)
+ # print(pay.get("name"), pay.get("doctype"))
+
+ if flt(pay.get("deposit")) > 0:
+ pay["deposit"] -= allocated_amount
+ elif flt(pay.get("deposit")) > 0:
+ pay["withdraw"] -= allocated_amount
+
+ bnk_st["unallocated_amount"] -= allocated_amount
+
+ entries.append(res)
+
+ # Break if pay is fully allocated
+ if pay.get("deposit") == 0 and pay.get("withdraw") == 0:
+ break
+
+ # Update the matching table
+ self.set("matching_table", [])
+ for entry in entries:
+ if entry["matched_amount"] != 0:
+ # print('rowwwww',entry["bank_transaction_id"])
+ row = self.append("matching_table", {})
+ row.update(entry)
+
+
+@frappe.whitelist()
+def get_bank_transaction(bank_account, company, from_statement_date=None, to_statement_date=None):
+ if from_statement_date and to_statement_date:
+ bank_transactn_list = frappe.db.get_all(
+ "Bank Transaction",
+ filters={
+ "date": ["between", [from_statement_date, to_statement_date]],
+ "bank_account": bank_account,
+ "company": company,
+ "status": "Unreconciled",
+ },
+ fields=[
+ "date",
+ "name",
+ "deposit",
+ "withdrawal",
+ "description",
+ "reference_number",
+ "unallocated_amount",
+ ],
+ )
+ else:
+ bank_transactn_list = frappe.db.get_all(
+ "Bank Transaction",
+ filters={"bank_account": bank_account, "company": company, "status": "Unreconciled"},
+ fields=[
+ "date",
+ "name",
+ "deposit",
+ "withdrawal",
+ "description",
+ "reference_number",
+ "unallocated_amount",
+ ],
+ )
+ return bank_transactn_list
+
+
+@frappe.whitelist()
+def get_erp_transaction(bank_account, company, from_statement_date=None, to_statement_date=None):
+ transaction_list = frappe.db.get_all(
+ "Bank Transaction", {"bank_account": bank_account, "status": "Unreconciled"}, pluck="name"
+ )
+ account = frappe.db.get_value("Bank Account", bank_account, 'account')
+ result = []
+ for i in transaction_list:
+ transaction = frappe.get_doc("Bank Transaction", i)
+ # print("++++++++++++++++++++++++++++++++")
+ # # print(transaction_list)
+ # print(get_linked_payments(transaction.name, document_types=["payment_entry","journal_entry"], from_date=from_statement_date, to_date=to_statement_date, filter_by_reference_date=None, from_reference_date=from_statement_date, to_reference_date=to_statement_date))
+ # print("++++++++++++++++++++++++++++++++")
+
+ for i in get_linked_payments(transaction.name, document_types=["payment_entry","journal_entry"], from_date=from_statement_date, to_date=to_statement_date, filter_by_reference_date=None, from_reference_date=from_statement_date, to_reference_date=to_statement_date):
+ if i['doctype'] == "Journal Entry":
+ list = frappe.db.get_all("Journal Entry Account",
+ {
+ 'account': account,
+ 'parent': i.name
+ },
+ ['credit_in_account_currency', 'debit_in_account_currency', 'parent']
+ )
+ for j in list:
+ if j['credit_in_account_currency'] > 0:
+ i['bank'] = 'Credit'
+ elif j['debit_in_account_currency'] > 0:
+ i['bank'] = 'Debit'
+ result.append(i)
+ else:
+ result.append(i)
+
+ return result
+ # if from_statement_date and to_statement_date:
+ # pe_list = frappe.db.get_all("Payment Entry",filters={'posting_date': ['between' ,[from_statement_date, to_statement_date]], 'bank_account':bank_account, 'company': company}, fields = ['posting_date', 'paid_amount', 'reference_no', 'payment_type'])
+ # je_list = frappe.db.get_all("Journal Entry Account", filters={'bank_account': bank_account}, fields = ["parent", "account", "creation", "debit", "credit"])
+ # else:
+ # pe_list = frappe.db.get_all("Payment Entry",filters={'bank_account':bank_account, 'company': company}, fields = ['posting_date', 'paid_amount', 'reference_no', 'payment_type'])
+ # je_list = frappe.db.get_all("Journal Entry Account", filters={'bank_account': bank_account}, fields = ["parent", "account", "creation", "debit", "credit"])
+ # return pe_list, je_list
+
+
+@frappe.whitelist()
+def reconcile_bnk_transaction(bank_transaction_id, amount, name, payment_document):
+ bnk_trn = frappe.get_doc("Bank Transaction", bank_transaction_id)
+ # print("***********************************")
+ # print("docc",bank_transaction_id)
+ # print("***********************************")
+ bnk_trn.append(
+ "payment_entries",
+ {"payment_document": payment_document, "payment_entry": name, "allocated_amount": flt(amount)},
+ )
+ try:
+ bnk_trn.save()
+ frappe.msgprint(_("Successfully Reconciled"))
+ except Exception as e:
+ frappe.msgprint("Please Reconcile again to ")
+
+
+@frappe.whitelist()
+def get_closing_bal(opening_bal, from_date, to_date, bank_account):
+ total_credits = (
+ frappe.db.sql(
+ """
+ SELECT SUM(deposit)
+ FROM `tabBank Transaction`
+ WHERE bank_account = %s AND date BETWEEN %s AND %s AND docstatus = 1
+ """,
+ (bank_account, from_date, to_date),
+ )[-1][-1]
+ or 0
+ )
+
+ # Sum of debits (withdrawals)
+ total_debits = (
+ frappe.db.sql(
+ """
+ SELECT SUM(withdrawal)
+ FROM `tabBank Transaction`
+ WHERE bank_account = %s AND date BETWEEN %s AND %s AND docstatus = 1
+ """,
+ (bank_account, from_date, to_date),
+ )[-1][-1]
+ or 0
+ )
+
+ # Calculate closing balance
+ closing_balance = float(opening_bal) + float(total_credits) or 0 - float(total_debits) or 0
+ return closing_balance
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/test_bank_reconciliation_tool_erpnext.py b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/test_bank_reconciliation_tool_erpnext.py
new file mode 100644
index 000000000000..0685beed5615
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool_erpnext/test_bank_reconciliation_tool_erpnext.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestBankReconciliationToolERPNext(FrappeTestCase):
+ pass
diff --git a/erpnext/accounts/doctype/bank_statement/__init__.py b/erpnext/accounts/doctype/bank_statement/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/erpnext/accounts/doctype/bank_statement/bank_statement.json b/erpnext/accounts/doctype/bank_statement/bank_statement.json
new file mode 100644
index 000000000000..fcaa56106703
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement/bank_statement.json
@@ -0,0 +1,79 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-01-08 14:52:55.060923",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "date",
+ "bank_transaction_id",
+ "description",
+ "deposit",
+ "column_break_ofcr",
+ "withdraw",
+ "reference_no",
+ "unallocated_amount"
+ ],
+ "fields": [
+ {
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Date",
+ "options": "Date"
+ },
+ {
+ "fieldname": "bank_transaction_id",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Bank Transaction ID"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Description"
+ },
+ {
+ "fieldname": "deposit",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Deposit"
+ },
+ {
+ "fieldname": "column_break_ofcr",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "withdraw",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Withdraw"
+ },
+ {
+ "fieldname": "reference_no",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Reference No"
+ },
+ {
+ "fieldname": "unallocated_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Unallocated Amount"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-01-10 10:17:54.674125",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Bank Statement",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_statement/bank_statement.py b/erpnext/accounts/doctype/bank_statement/bank_statement.py
new file mode 100644
index 000000000000..38c9fd9a525b
--- /dev/null
+++ b/erpnext/accounts/doctype/bank_statement/bank_statement.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class BankStatement(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ bank_transaction_id: DF.Data | None
+ date: DF.Date | None
+ deposit: DF.Currency
+ description: DF.SmallText | None
+ parent: DF.Data
+ parentfield: DF.Data
+ parenttype: DF.Data
+ reference_no: DF.Data | None
+ unallocated_amount: DF.Currency
+ withdraw: DF.Currency
+ # end: auto-generated types
+ pass
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index 0e5133e40fb7..96424fee1168 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -496,13 +496,17 @@ def get_actual_expense(args):
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {}
if monthly_distribution:
- for d in frappe.db.sql(
- """select mdp.month, mdp.percentage_allocation
- from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
- where mdp.parent=md.name and md.fiscal_year=%s""",
- fiscal_year,
- as_dict=1,
- ):
+ mdp = frappe.qb.DocType("Monthly Distribution Percentage")
+ md = frappe.qb.DocType("Monthly Distribution")
+ res = (
+ frappe.qb.from_(mdp)
+ .join(md)
+ .on(mdp.parent == md.name)
+ .select(mdp.month, mdp.percentage_allocation)
+ .where(md.fiscal_year == fiscal_year)
+ .where(md.name == monthly_distribution)
+ )
+ for d in res:
distribution.setdefault(d.month, d.percentage_allocation)
dt = frappe.get_cached_value("Fiscal Year", fiscal_year, "year_start_date")
diff --git a/erpnext/accounts/doctype/erp_transaction/__init__.py b/erpnext/accounts/doctype/erp_transaction/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/erpnext/accounts/doctype/erp_transaction/erp_transaction.json b/erpnext/accounts/doctype/erp_transaction/erp_transaction.json
new file mode 100644
index 000000000000..f069eb7f7d98
--- /dev/null
+++ b/erpnext/accounts/doctype/erp_transaction/erp_transaction.json
@@ -0,0 +1,79 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-01-08 14:59:37.654994",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "date",
+ "reference_id",
+ "withdraw",
+ "column_break_mtjz",
+ "remaining_amount",
+ "reference_number",
+ "deposit",
+ "reference_doc"
+ ],
+ "fields": [
+ {
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Date"
+ },
+ {
+ "fieldname": "reference_id",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Reference ID"
+ },
+ {
+ "fieldname": "withdraw",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Withdraw"
+ },
+ {
+ "fieldname": "column_break_mtjz",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "remaining_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Remaining Amount"
+ },
+ {
+ "fieldname": "reference_number",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Reference Number"
+ },
+ {
+ "fieldname": "deposit",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Deposit"
+ },
+ {
+ "fieldname": "reference_doc",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Reference Doc",
+ "options": "Payment Entry\nJournal Entry"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-01-10 15:13:55.310249",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "ERP Transaction",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/erp_transaction/erp_transaction.py b/erpnext/accounts/doctype/erp_transaction/erp_transaction.py
new file mode 100644
index 000000000000..ca8619953e40
--- /dev/null
+++ b/erpnext/accounts/doctype/erp_transaction/erp_transaction.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class ERPTransaction(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ date: DF.Date | None
+ deposit: DF.Currency
+ parent: DF.Data
+ parentfield: DF.Data
+ parenttype: DF.Data
+ reference_doc: DF.Literal["Payment Entry", "Journal Entry"]
+ reference_id: DF.Data | None
+ reference_number: DF.Data | None
+ remaining_amount: DF.Currency
+ withdraw: DF.Currency
+ # end: auto-generated types
+ pass
diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
index f99c58bd91c1..716a20699eec 100644
--- a/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
+++ b/erpnext/accounts/doctype/exchange_rate_revaluation/test_exchange_rate_revaluation.py
@@ -740,35 +740,45 @@ def gain_loss_account(company:str):
def create_account(**args):
account_name = args.get('account_name')
- company = args.get('company', "_Test Company")
+ if not account_name:
+ return
+ company = args.get('company', " ")
+
existing_account = frappe.db.exists("Account", {
"name": f"{account_name} - _TC"
})
+
if not existing_account:
try:
doc = frappe.get_doc({
"doctype": "Account",
- "company": company,
"account_type": args.get('account_type', " "),
"account_name": account_name,
- "parent_account": args.get('parent_account'),
"report_type": args.get('report_type', "Balance Sheet"),
"root_type": args.get('root_type', "Liability"),
"account_currency": args.get('account_currency', "INR"),
- }).insert()
-
+ "is_group": args.get('is_group', 0)
+ })
+ if args.get('parent_account'):
+ doc.parent_account = args.get('parent_account')
+ if args.get('company'):
+ doc.company = args.get('company')
+ doc.insert(ignore_mandatory=True)
frappe.db.commit()
- print(f"Account {account_name} created successfully.")
except Exception as e:
frappe.log_error(f"Account Creation Failed: {account_name}", str(e))
- print(f"Error creating account '{account_name}': {str(e)}")
- else:
- print(f"Account '{account_name} _TC' already exists.")
+
+
def create_records_for_err():
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_supplier
- from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_customer
+
+ create_warehouse(
+ warehouse_name="_Test Warehouse",
+ company="_Test Company"
+ )
+
create_account(
account_name="_Test Payable USD",
parent_account="Current Assets - _TC",
@@ -797,7 +807,7 @@ def create_records_for_err():
report_type="Balance Sheet",
)
create_cost_center(
- cost_center_name="_Test Cost Center - _TC",
+ cost_center_name="_Test Cost Center",
company="_Test Company",
parent_cost_center="_Test Company - _TC"
)
@@ -838,4 +848,35 @@ def create_records_for_err():
},
)
supplier.save()
- frappe.db.commit()
\ No newline at end of file
+ frappe.db.commit()
+
+def create_warehouse(**args):
+ warehouse_name = args.get('warehouse_name')
+ company = args.get('company', "_Test Company")
+ full_warehouse_name = f"{warehouse_name} - _TC"
+ if not frappe.db.exists("Warehouse", full_warehouse_name):
+ frappe.get_doc(
+ {
+ "doctype": "Warehouse",
+ "warehouse_name": warehouse_name,
+ "company": company,
+ }
+ ).insert(ignore_mandatory=True)
+ frappe.db.commit()
+
+
+
+def create_cost_center(**args):
+ args = frappe._dict(args)
+ if args.cost_center_name:
+ company = args.company or "_Test Company"
+ company_abbr = frappe.db.get_value("Company", company, "abbr")
+ cc_name = args.cost_center_name + " - " + company_abbr
+ if not frappe.db.exists("Cost Center", cc_name):
+ cc = frappe.new_doc("Cost Center")
+ cc.company = args.company or "_Test Company"
+ cc.cost_center_name = args.cost_center_name
+ cc.is_group = args.is_group or 0
+ cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
+ cc.insert()
+ frappe.db.commit()
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_closing/gl_closing.py b/erpnext/accounts/doctype/gl_closing/gl_closing.py
index 1316c1c858b1..62ec10da9d08 100644
--- a/erpnext/accounts/doctype/gl_closing/gl_closing.py
+++ b/erpnext/accounts/doctype/gl_closing/gl_closing.py
@@ -104,10 +104,17 @@ def get_merge():
.run(as_dict=True)
)
return data
+
+
@frappe.whitelist(allow_guest=True)
def get_account_settings():
- acs = frappe.get_all("Accounts Closing Table", fields=["company", "frozen_accounts_modifier"])
+ if frappe.db.table_exists("Accounts Closing Table"):
+ acs = frappe.get_all("Accounts Closing Table", fields=["company", "frozen_accounts_modifier"])
+ else:
+ acs = []
return acs
+
+
@frappe.whitelist(allow_guest=True)
def get_user_roles():
user = frappe.session.user
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 8678a49f1ee5..6553deaf4af6 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -294,6 +294,9 @@ def validate_party(self):
validate_account_party_type(self)
def validate_currency(self):
+ if self.is_cancelled:
+ return
+
company_currency = erpnext.get_company_currency(self.company)
account_currency = get_account_currency(self.account)
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index e4cd0047b8a6..d5a90af2e64e 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -593,7 +593,7 @@ def test_select_tds_payable_and_creditors_account_TC_ACC_024(self):
create_records('_Test Supplier TDS')
supplier = frappe.get_doc("Supplier", "_Test Supplier TDS")
- account = frappe.get_doc("Account", "Test TDS Payable - _TC")
+ account = frappe.get_doc("Account", "_Test TDS Payable - _TC")
if supplier and account:
jv=frappe.new_doc("Journal Entry")
@@ -607,7 +607,7 @@ def test_select_tds_payable_and_creditors_account_TC_ACC_024(self):
"credit_in_account_currency": 1000
},
{
- "account": 'Test Creditors - _TC',
+ "account": 'Creditors - _TC',
"party_type": "Supplier",
"party": supplier.name,
"debit_in_account_currency": 1000,
@@ -628,7 +628,7 @@ def test_select_tds_payable_and_creditors_account_TC_ACC_024(self):
self.expected_gle = [
{
- "account": 'Test Creditors - _TC',
+ "account": 'Creditors - _TC',
"debit_in_account_currency": 1000,
"credit_in_account_currency": 0,
"cost_center": "Main - _TC",
@@ -861,6 +861,307 @@ def test_deferred_expense_entry_TC_ACC_055(self):
self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+ def test_deferred_expense_entry_TC_ACC_056(self):
+ # Set up input parameters
+ entry_type = "Deferred Expense"
+ debit_account = "Write Off - _TC"
+ credit_account = "_Test Accumulated Depreciations - _TC"
+ amount = 30000.0
+
+ # Create the Journal Entry using the existing function
+ jv = make_journal_entry(
+ account1=debit_account,
+ account2=credit_account,
+ amount=amount,
+ save=False,
+ submit=False,
+ )
+ # Set the entry type and save the journal entry
+ jv.entry_type = entry_type
+ jv.save().submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": credit_account, "debit": 0, "credit": amount},
+ {"account": debit_account, "debit": amount, "credit": 0}
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 2, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], "Account mismatch in GL Entry.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+
+ def test_deferred_revenue_entry_TC_ACC_057(self):
+ # Set up input parameters
+ entry_type = "Deferred Revenue"
+ debit_account = "Debtors - _TC"
+ credit_account = "Creditors - _TC"
+ amount = 30000.0
+
+ # Create the Journal Entry using the existing function
+ jv = make_journal_entry(
+ account1=debit_account,
+ account2=credit_account,
+ amount=amount,
+ save=False,
+ submit=False,
+ )
+ for account in jv.accounts:
+ if account.account == "Creditors - _TC":
+ account.party_type = "Supplier"
+ account.party = "_Test Supplier"
+
+ elif account.account == "Debtors - _TC":
+ account.party_type = "Customer"
+ account.party = "_Test Customer"
+ # Set the entry type and save the journal entry
+ jv.entry_type = entry_type
+ jv.save().submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": credit_account, "debit": 0, "credit": amount},
+ {"account": debit_account, "debit": amount, "credit": 0}
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 2, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], "Account mismatch in GL Entry.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+
+ def test_deferred_revenue_entry_TC_ACC_058(self):
+ # Set up input parameters
+ entry_type = "Deferred Revenue"
+ debit_account = "Creditors - _TC"
+ credit_account = "Sales - _TC"
+ amount = 30000.0
+
+ # Create the Journal Entry using the existing function
+ jv = make_journal_entry(
+ account1=debit_account,
+ account2=credit_account,
+ amount=amount,
+ save=False,
+ submit=False,
+ )
+ for account in jv.accounts:
+ if account.account == "Creditors - _TC":
+ account.party_type = "Supplier"
+ account.party = "_Test Supplier"
+ # Set the entry type and save the journal entry
+ jv.entry_type = entry_type
+ jv.save().submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": debit_account, "debit": amount, "credit": 0},
+ {"account": credit_account, "debit": 0, "credit": amount}
+
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 2, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], "Account mismatch in GL Entry.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+
+ def test_reversal_of_itc_TC_ACC_059(self):
+ # Set up input parameters
+ entry_type = "Reversal of ITC"
+ debit_account_sgst = "Input Tax SGST - _TC"
+ debit_account_cgst = "Input Tax CGST - _TC"
+ credit_account = "Creditors - _TC"
+ amount_sgst = 5000.0
+ amount_cgst = 5000.0
+
+ # Create the Journal Entry
+ jv = frappe.new_doc("Journal Entry")
+ jv.posting_date = nowdate()
+ jv.company = "_Test Company"
+ jv.entry_type = entry_type
+ jv.user_remark = "Reversal of ITC Test Case"
+
+ # Add accounts to the Journal Entry
+ jv.append("accounts", {
+ "account": debit_account_sgst,
+ "debit_in_account_currency": amount_sgst,
+ "credit_in_account_currency": 0,
+ "cost_center": "_Test Cost Center - _TC"
+ })
+
+ jv.append("accounts", {
+ "account": debit_account_cgst,
+ "debit_in_account_currency": amount_cgst,
+ "credit_in_account_currency": 0,
+ "cost_center": "_Test Cost Center - _TC"
+ })
+
+ jv.append("accounts", {
+ "account": credit_account,
+ "debit_in_account_currency": 0,
+ "credit_in_account_currency": amount_sgst + amount_cgst,
+ "cost_center": "_Test Cost Center - _TC",
+ "party_type": "Supplier",
+ "party": "_Test Supplier"
+ })
+
+ # Save and submit the Journal Entry
+ jv.insert()
+ jv.submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": credit_account, "debit": 0, "credit": amount_sgst + amount_cgst},
+ {"account": debit_account_cgst, "debit": amount_cgst, "credit": 0},
+ {"account": debit_account_sgst, "debit": amount_sgst, "credit": 0},
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 3, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], f"Account mismatch in GL Entry: {entry['account']}.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+
+ def test_exchange_gain_or_loss_TC_ACC_060(self):
+ # Set up input parameters
+ entry_type = "Exchange Gain or Loss"
+ debit_account = "Exchange Gain/Loss - _TC"
+ credit_account = "Debtors - _TC"
+ party_type = "Customer"
+ party = "_Test Customer"
+ new_exchange_rate = 75.0
+ amount = 1000.0
+
+ # Create the Journal Entry using the make_journal_entry method
+ jv = make_journal_entry(
+ account1=debit_account,
+ account2=credit_account,
+ amount=amount,
+ save=False,
+ submit=False
+ )
+
+ for account in jv.accounts:
+ if account.account == credit_account:
+ account.party_type = party_type
+ account.party = party
+ account.exchange_rate = new_exchange_rate
+
+ # Set the entry type and save the journal entry
+ jv.entry_type = entry_type
+ jv.save().submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": credit_account, "debit": 0, "credit": amount},
+ {"account": debit_account, "debit": amount, "credit": 0},
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 2, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], f"Account mismatch in GL Entry: {entry['account']}.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
+
+ def test_exchange_rate_revaluation_TC_ACC_061(self):
+ # Set up input parameters
+ entry_type = "Exchange Rate Revaluation"
+ debit_account = "Creditors - _TC"
+ credit_account = "Exchange Gain/Loss - _TC"
+ party_type = "Supplier"
+ party = "_Test Supplier"
+ new_exchange_rate = 80.0
+ amount = 2000.0
+
+ # Create the Journal Entry
+ jv = make_journal_entry(
+ account1=debit_account,
+ account2=credit_account,
+ amount=amount,
+ save=False,
+ submit=False
+ )
+
+ for account in jv.accounts:
+ if account.account == debit_account:
+ account.party_type = party_type
+ account.party = party
+ account.exchange_rate = new_exchange_rate
+
+ # Set the entry type and save the journal entry
+ jv.entry_type = entry_type
+ jv.save().submit()
+
+ # Fetch GL Entries to validate the transaction
+ gl_entries = frappe.db.sql(
+ """SELECT account, debit, credit FROM `tabGL Entry`
+ WHERE voucher_type='Journal Entry' AND voucher_no=%s
+ ORDER BY account""",
+ jv.name,
+ as_dict=True,
+ )
+
+ # Expected GL entries
+ expected_gl_entries = [
+ {"account": debit_account, "debit": amount, "credit": 0},
+ {"account": credit_account, "debit": 0, "credit": amount},
+ ]
+
+ # Assertions
+ self.assertEqual(len(gl_entries), 2, "Incorrect number of GL entries created.")
+ for entry, expected in zip(gl_entries, expected_gl_entries):
+ self.assertEqual(entry["account"], expected["account"], f"Account mismatch in GL Entry: {entry['account']}.")
+ self.assertEqual(entry["debit"], expected["debit"], f"Debit mismatch for {entry['account']}.")
+ self.assertEqual(entry["credit"], expected["credit"], f"Credit mismatch for {entry['account']}.")
diff --git a/erpnext/accounts/doctype/matching_table/__init__.py b/erpnext/accounts/doctype/matching_table/__init__.py
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/erpnext/accounts/doctype/matching_table/matching_table.json b/erpnext/accounts/doctype/matching_table/matching_table.json
new file mode 100644
index 000000000000..2b30b8775dbd
--- /dev/null
+++ b/erpnext/accounts/doctype/matching_table/matching_table.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2025-01-08 15:13:57.714637",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "bank_transaction_id",
+ "reference_to",
+ "matched_amount",
+ "reference_id"
+ ],
+ "fields": [
+ {
+ "fieldname": "bank_transaction_id",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Bank Transaction ID",
+ "options": "Bank Transaction"
+ },
+ {
+ "default": "Payment Entry",
+ "fieldname": "reference_to",
+ "fieldtype": "Select",
+ "label": "Reference To",
+ "options": "Payment Entry\nJournal Entry"
+ },
+ {
+ "fieldname": "matched_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Matched Amount"
+ },
+ {
+ "fieldname": "reference_id",
+ "fieldtype": "Dynamic Link",
+ "in_list_view": 1,
+ "label": "Reference ID",
+ "options": "reference_to"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2025-01-10 10:17:47.952899",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Matching Table",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/matching_table/matching_table.py b/erpnext/accounts/doctype/matching_table/matching_table.py
new file mode 100644
index 000000000000..477d8a914245
--- /dev/null
+++ b/erpnext/accounts/doctype/matching_table/matching_table.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class MatchingTable(Document):
+ # begin: auto-generated types
+ # This code is auto-generated. Do not modify anything in this block.
+
+ from typing import TYPE_CHECKING
+
+ if TYPE_CHECKING:
+ from frappe.types import DF
+
+ bank_transaction_id: DF.Link | None
+ matched_amount: DF.Currency
+ parent: DF.Data
+ parentfield: DF.Data
+ parenttype: DF.Data
+ reference_id: DF.DynamicLink | None
+ reference_to: DF.Literal["Payment Entry", "Journal Entry"]
+ # end: auto-generated types
+ pass
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index ac38e729c261..d4be744509ca 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -2422,17 +2422,16 @@ def get_orders_to_be_billed(
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
-
orders = frappe.db.sql(
f"""
SELECT
name AS voucher_no,
CASE
- WHEN {rounded_total_field} IS NOT NULL THEN {rounded_total_field}
+ WHEN {rounded_total_field} > 0 THEN {rounded_total_field}
ELSE {grand_total_field}
END AS invoice_amount,
(CASE
- WHEN {rounded_total_field} IS NOT NULL THEN {rounded_total_field}
+ WHEN {rounded_total_field} > 0 THEN {rounded_total_field}
ELSE {grand_total_field}
END - advance_paid) AS outstanding_amount,
transaction_date AS posting_date
@@ -2444,7 +2443,7 @@ def get_orders_to_be_billed(
AND company = %s
AND status != 'Closed'
AND (CASE
- WHEN {rounded_total_field} IS NOT NULL THEN {rounded_total_field}
+ WHEN {rounded_total_field} > 0 THEN {rounded_total_field}
ELSE {grand_total_field}
END) > advance_paid
AND ABS(100 - per_billed) > 0.01
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index 9b118fabb1c5..55215982add1 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -24,6 +24,7 @@
)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.doctype.employee.test_employee import make_employee
+import frappe.utils
test_dependencies = ["Item"]
@@ -1921,7 +1922,7 @@ def test_apply_tax_withholding_category_TC_ACC_021(self):
tax_withholding_category=frappe.get_doc("Tax Withholding Category","Test - TDS - 194C - Company")
if len(tax_withholding_category.accounts) >0:
- self.assertEqual(tax_withholding_category.accounts[0].account,"Test TDS Payable - _TC")
+ self.assertEqual(tax_withholding_category.accounts[0].account,"_Test TDS Payable - _TC")
payment_entry=create_payment_entry(
party_type="Supplier",
@@ -1937,7 +1938,7 @@ def test_apply_tax_withholding_category_TC_ACC_021(self):
payment_entry.append(
"taxes",
{
- "account_head": "Test TDS Payable - _TC",
+ "account_head": "_Test TDS Payable - _TC",
"charge_type": "On Paid Amount",
"rate": 0,
"add_deduct_tax": "Deduct",
@@ -1960,7 +1961,7 @@ def test_apply_tax_withholding_category_TC_ACC_021(self):
'against_voucher': None
},
{
- 'account': 'Test TDS Payable - _TC',
+ 'account': '_Test TDS Payable - _TC',
'debit': 0.0, 'credit': 1600.0,
'against_voucher': None
}
@@ -1977,7 +1978,7 @@ def test_link_advance_payment_with_purchase_invoice_TC_ACC_022(self):
tax_withholding_category=frappe.get_doc("Tax Withholding Category","Test - TDS - 194C - Company")
if len(tax_withholding_category.accounts) >0:
- self.assertEqual(tax_withholding_category.accounts[0].account,"Test TDS Payable - _TC")
+ self.assertEqual(tax_withholding_category.accounts[0].account,"_Test TDS Payable - _TC")
payment_entry=create_payment_entry(
party_type="Supplier",
@@ -1993,7 +1994,7 @@ def test_link_advance_payment_with_purchase_invoice_TC_ACC_022(self):
payment_entry.append(
"taxes",
{
- "account_head": "Test TDS Payable - _TC",
+ "account_head": "_Test TDS Payable - _TC",
"charge_type": "On Paid Amount",
"rate": 0,
"add_deduct_tax": "Deduct",
@@ -2055,7 +2056,7 @@ def test_link_advance_payment_with_purchase_invoice_TC_ACC_022(self):
"credit_in_account_currency": 0.0
},
{
- "account": "Test TDS Payable - _TC",
+ "account": "_Test TDS Payable - _TC",
"cost_center": "Main - _TC",
"account_currency": "INR",
"debit": 0.0,
@@ -2199,7 +2200,11 @@ def create_supplier(**args):
args = frappe._dict(args)
if frappe.db.exists("Supplier", args.supplier_name):
- return frappe.get_doc("Supplier", args.supplier_name)
+ doc = frappe.get_doc("Supplier", args.supplier_name)
+ if doc.name == "_Test Supplier USD" and not frappe.db.exists("Party Account", {"parent": doc.name, "account": "_Test Payable USD - _TC"}):
+ doc.append("accounts", {"company": args.company, "account": "_Test Payable USD - _TC"})
+ frappe.db.commit()
+ return doc
doc = frappe.get_doc(
{
"doctype": "Supplier",
@@ -2212,104 +2217,131 @@ def create_supplier(**args):
)
doc.append('accounts',{
'company': args.company,
- 'account': '_Test Payable USD - _TC' if args.default_currency == 'USD' else 'Test TDS Payable',
+ 'account': '_Test Payable USD - _TC' if args.default_currency == 'USD' else '_Test TDS Payable - _TC',
})
-
if not args.without_supplier_group:
doc.supplier_group = args.supplier_group or "Services"
+
- doc.insert()
- doc.save()
+ doc.insert(ignore_mandatory=True)
frappe.db.commit()
return doc
def create_account():
- accounts = [
- {"name": "Current Liabilities", "parent": "Source of Funds (Liabilities) - _TC"},
- {"name": "Duties and Taxes", "parent": "Current Liabilities - _TC"},
- {"name": "Test TDS Payable", "parent": "Duties and Taxes - _TC"},
- {"name": "Test Creditors", "parent": "Accounts Payable - _TC"},
- {"name": "_Test Payable USD", "parent": "Current Liabilities - _TC"},
- {"name": "_Test Cash", "parent": "Cash In Hand - _TC"},
- ]
-
- for account in accounts:
- if not frappe.db.exists("Account", f"{account['name']} - _TC"): # Ensure proper check with "- _TC"
- try:
- doc = frappe.get_doc({
- "doctype": "Account",
- "company": "_Test Company",
- "account_name": account["name"],
- "parent_account": account["parent"],
- "report_type": "Balance Sheet",
- "root_type": "Liability",
- "account_currency": "USD" if account["name"] == "_Test Payable USD" else "INR",
- }).insert()
- frappe.db.commit()
- except Exception as e:
- frappe.log_error(f"Failed to insert {account['name']}", str(e))
+ accounts = [
+ {"name": "Source of Funds (Liabilities)", "parent": ""},
+ {"name": "Current Liabilities", "parent": "Source of Funds (Liabilities) - _TC"},
+ {"name": "Duties and Taxes", "parent": "Current Liabilities - _TC"},
+ {"name": "_Test TDS Payable", "parent": "Duties and Taxes - _TC","account_type":"Tax"},
+ {"name": "_Test Creditors", "parent": "Accounts Payable - _TC","account_type":"Payable"},
+ {"name": "_Test Payable USD", "parent": "Accounts Payable - _TC","account_type":"Payable"},
+ {"name": "_Test Cash", "parent": "Cash In Hand - _TC"},
+ ]
+
+ if not frappe.db.exists("Company", "_Test Company"):
+ return
+
+ for account in accounts:
+ if frappe.db.exists("Account", f"{account['name']} - _TC"):
+ continue
+
+ if account["parent"] and not frappe.db.exists("Account", account["parent"]):
+ continue
+
+ try:
+ doc = frappe.new_doc("Account")
+ doc.company = "_Test Company"
+ doc.account_name = account["name"]
+ doc.report_type = "Balance Sheet"
+ doc.root_type = "Liability"
+ doc.is_group = 1 if account["name"] == 'Source of Funds' else 0
+ doc.account_currency = "USD" if account["name"] == "_Test Payable USD" else "INR"
+
+ # Set count_type based on account name
+ if account["account_type"]:
+ doc.account_type = account["account_type"]
-def create_records(supplier):
- from erpnext.accounts.doctype.tax_withholding_category.test_tax_withholding_category import create_tax_withholding_category
+ if account["parent"]:
+ doc.parent_account = account["parent"]
+
+ doc.insert(ignore_mandatory=True)
+ frappe.db.commit()
- create_account()
+ except Exception as e:
+ frappe.log_error(f"Failed to insert {account['name']}", str(e))
- create_tax_withholding_category(
- category_name="Test - TDS - 194C - Company",
- rate=2,
- from_date=frappe.utils.get_date_str('01-04-2024'),
- to_date=frappe.utils.get_date_str('31-03-2025'),
- account="Test TDS Payable - _TC",
- single_threshold=30000,
- cumulative_threshold=100000,
- consider_party_ledger_amount=1,
- )
- create_supplier(
- supplier_name=supplier,
- company="_Test Company",
- tax_withholding_category="Test - TDS - 194C - Company",
- default_currency="USD" if supplier == "_Test Supplier USD" else "INR",
- )
+def create_records(supplier):
+ from erpnext.accounts.doctype.tax_withholding_category.test_tax_withholding_category import create_tax_withholding_category
+ create_company()
+
+ create_account()
+
+ create_tax_withholding_category(
+ category_name="Test - TDS - 194C - Company",
+ rate=2,
+ from_date=frappe.utils.get_date_str('01-04-2024'),
+ to_date=frappe.utils.get_date_str('31-03-2025'),
+ account="_Test TDS Payable - _TC",
+ single_threshold=30000,
+ cumulative_threshold=100000,
+ consider_party_ledger_amount=1,
+ )
+ create_supplier(
+ supplier_name=supplier,
+ company="_Test Company",
+ tax_withholding_category="Test - TDS - 194C - Company",
+ default_currency="USD" if supplier == "_Test Supplier USD" else "INR",
+ )
- frappe.db.commit()
+ frappe.db.commit()
def make_test_item(item_name=None):
- from erpnext.stock.doctype.item.test_item import make_item
+ from erpnext.stock.doctype.item.test_item import make_item
+ app_name = "india_compliance"
+ if not frappe.db.exists("Item", item_name or "Test Item with Tax"):
+ if app_name in frappe.get_installed_apps():
+ if not frappe.db.exists("GST HSN Code", '888890'):
+ frappe.get_doc({
+ "doctype": 'GST HSN Code',
+ "hsn_code": '888890',
+ "description": 'test'
+ }).insert()
+ frappe.db.commit()
+
+ item= make_item(
+ item_name or "Test Item with Tax",
+ {
+ "is_stock_item": 1,
+ "gst_hsn_code": "888890",
+ },
+ )
+
+ return item
- if not frappe.db.exists("Item", item_name or "Test Item with Tax"):
- app_name = "india_compliance"
-
- if app_name in frappe.get_installed_apps():
- if not frappe.db.exists("GST HSN Code", '888890'):
- frappe.get_doc({
- "doctype": 'GST HSN Code',
- "hsn_code": '888890',
- "description": 'test'
- }).insert()
- frappe.db.commit()
-
- item= make_item(
- item_name or "Test Item with Tax",
- {
- "is_stock_item": 1,
- "gst_hsn_code": "888890",
- },
- )
-
- return item
-
- else:
- item= make_item(
- "Test TDS Item",
- {
- "is_stock_item": 1,
- },
- )
- return item
- else:
- return frappe.get_doc("Item", item_name or "Test Item with Tax")
+ else:
+ item= make_item(
+ "Test TDS Item",
+ {
+ "is_stock_item": 1,
+ },
+ )
+ return item
+ else:
+ if app_name in frappe.get_installed_apps():
+ if not frappe.db.exists("GST HSN Code", '888890'):
+ frappe.get_doc({
+ "doctype": 'GST HSN Code',
+ "hsn_code": '888890',
+ "description": 'test'
+ }).insert()
+ item=frappe.get_doc("Item", item_name or "Test Item with Tax")
+ if not item.gst_hsn_code:
+ item.gst_hsn_code="888890"
+ item.save()
+ frappe.db.commit()
+ return item
def create_purchase_invoice(**args):
# return sales invoice doc object
@@ -2340,4 +2372,16 @@ def create_purchase_invoice(**args):
)
pi.save()
- return pi
\ No newline at end of file
+ return pi
+
+def create_company():
+ if not frappe.db.exists("Company", "_Test Company"):
+ frappe.get_doc({
+ "doctype": "Company",
+ "company_name": "_Test Company",
+ "company_type": "Company",
+ "default_currency": "INR",
+ "company_email": "test@example.com",
+ "abbr":"_TC"
+ }).insert()
+ frappe.db.commit()
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js
index 50f96a4e2b6b..b5b61a23f09b 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.js
+++ b/erpnext/accounts/doctype/payment_request/payment_request.js
@@ -93,3 +93,7 @@ frappe.ui.form.on("Payment Request", "is_a_subscription", function (frm) {
});
}
});
+
+frappe.ui.form.on("Payment Request", "is_payment_order_required", function (frm) {
+ frm.toggle_reqd("bank_account", frm.doc.is_payment_order_required);
+})
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 273e814815bc..dd8bde0b4a7d 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -1,468 +1,477 @@
{
- "actions": [],
- "autoname": "naming_series:",
- "creation": "2015-12-15 22:23:24.745065",
- "doctype": "DocType",
- "engine": "InnoDB",
- "field_order": [
- "payment_request_type",
- "transaction_date",
- "column_break_2",
- "naming_series",
- "company",
- "mode_of_payment",
- "party_details",
- "party_type",
- "party",
- "party_name",
- "column_break_4",
- "reference_doctype",
- "reference_name",
- "transaction_details",
- "grand_total",
- "currency",
- "is_a_subscription",
- "column_break_18",
- "outstanding_amount",
- "party_account_currency",
- "subscription_section",
- "subscription_plans",
- "bank_account_details",
- "bank_account",
- "bank",
- "column_break_11",
- "iban",
- "branch_code",
- "swift_number",
- "accounting_dimensions_section",
- "cost_center",
- "dimension_col_break",
- "recipient_and_message",
- "print_format",
- "email_to",
- "subject",
- "column_break_9",
- "payment_gateway_account",
- "status",
- "make_sales_invoice",
- "section_break_10",
- "message",
- "message_examples",
- "mute_email",
- "payment_url",
- "section_break_7",
- "payment_gateway",
- "payment_account",
- "payment_channel",
- "payment_order",
- "amended_from"
- ],
- "fields": [
- {
- "default": "Inward",
- "fieldname": "payment_request_type",
- "fieldtype": "Select",
- "label": "Payment Request Type",
- "options": "Outward\nInward",
- "reqd": 1
- },
- {
- "fieldname": "transaction_date",
- "fieldtype": "Date",
- "in_preview": 1,
- "label": "Transaction Date"
- },
- {
- "fieldname": "column_break_2",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Series",
- "no_copy": 1,
- "options": "ACC-PRQ-.YYYY.-",
- "print_hide": 1,
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "mode_of_payment",
- "fieldtype": "Link",
- "label": "Mode of Payment",
- "options": "Mode of Payment"
- },
- {
- "fieldname": "party_details",
- "fieldtype": "Section Break",
- "label": "Party Details"
- },
- {
- "fieldname": "party_type",
- "fieldtype": "Link",
- "label": "Party Type",
- "options": "DocType"
- },
- {
- "fieldname": "party",
- "fieldtype": "Dynamic Link",
- "label": "Party",
- "options": "party_type"
- },
- {
- "fieldname": "column_break_4",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "reference_doctype",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Reference Doctype",
- "no_copy": 1,
- "options": "DocType",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "reference_name",
- "fieldtype": "Dynamic Link",
- "in_global_search": 1,
- "in_standard_filter": 1,
- "label": "Reference Name",
- "no_copy": 1,
- "options": "reference_doctype",
- "print_hide": 1,
- "read_only": 1,
- "search_index": 1
- },
- {
- "fieldname": "transaction_details",
- "fieldtype": "Section Break",
- "label": "Transaction Details"
- },
- {
- "description": "Amount in transaction currency",
- "fieldname": "grand_total",
- "fieldtype": "Currency",
- "in_preview": 1,
- "label": "Amount",
- "non_negative": 1,
- "options": "currency",
- "reqd": 1
- },
- {
- "default": "0",
- "fieldname": "is_a_subscription",
- "fieldtype": "Check",
- "label": "Is a Subscription"
- },
- {
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "currency",
- "fieldtype": "Link",
- "label": "Transaction Currency",
- "options": "Currency",
- "read_only": 1
- },
- {
- "depends_on": "eval:doc.is_a_subscription",
- "fieldname": "subscription_section",
- "fieldtype": "Section Break",
- "label": "Subscription Section"
- },
- {
- "fieldname": "subscription_plans",
- "fieldtype": "Table",
- "label": "Subscription Plans",
- "options": "Subscription Plan Detail"
- },
- {
- "collapsible": 1,
- "fieldname": "bank_account_details",
- "fieldtype": "Section Break",
- "label": "Bank Account Details"
- },
- {
- "fieldname": "bank_account",
- "fieldtype": "Link",
- "label": "Bank Account",
- "options": "Bank Account"
- },
- {
- "fetch_from": "bank_account.bank",
- "fieldname": "bank",
- "fieldtype": "Link",
- "label": "Bank",
- "options": "Bank",
- "read_only": 1
- },
- {
- "fetch_from": "bank_account.bank_account_no",
- "fieldname": "bank_account_no",
- "fieldtype": "Read Only",
- "label": "Bank Account No"
- },
- {
- "fetch_from": "bank_account.account",
- "fieldname": "account",
- "fieldtype": "Read Only",
- "label": "Account"
- },
- {
- "fieldname": "column_break_11",
- "fieldtype": "Column Break"
- },
- {
- "fetch_from": "bank_account.iban",
- "fieldname": "iban",
- "fieldtype": "Read Only",
- "label": "IBAN"
- },
- {
- "fetch_from": "bank_account.branch_code",
- "fetch_if_empty": 1,
- "fieldname": "branch_code",
- "fieldtype": "Read Only",
- "label": "Branch Code"
- },
- {
- "fetch_from": "bank.swift_number",
- "fieldname": "swift_number",
- "fieldtype": "Read Only",
- "label": "SWIFT Number"
- },
- {
- "collapsible": 1,
- "fieldname": "accounting_dimensions_section",
- "fieldtype": "Section Break",
- "label": "Accounting Dimensions"
- },
- {
- "fieldname": "cost_center",
- "fieldtype": "Link",
- "label": "Cost Center",
- "options": "Cost Center"
- },
- {
- "fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval: doc.payment_request_type == 'Inward'",
- "fieldname": "recipient_and_message",
- "fieldtype": "Section Break",
- "label": "Recipient Message And Payment Details"
- },
- {
- "depends_on": "eval: doc.payment_channel != \"Phone\"",
- "fieldname": "print_format",
- "fieldtype": "Select",
- "label": "Print Format"
- },
- {
- "fieldname": "email_to",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "To"
- },
- {
- "depends_on": "eval: doc.payment_channel != \"Phone\"",
- "fieldname": "subject",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "Subject"
- },
- {
- "fieldname": "column_break_9",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval: doc.payment_request_type == 'Inward'",
- "fieldname": "payment_gateway_account",
- "fieldtype": "Link",
- "label": "Payment Gateway Account",
- "options": "Payment Gateway Account"
- },
- {
- "default": "Draft",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "options": "\nDraft\nRequested\nInitiated\nPartially Paid\nPayment Ordered\nPaid\nFailed\nCancelled",
- "read_only": 1
- },
- {
- "default": "0",
- "depends_on": "eval:doc.reference_doctype==\"Sales Order\"",
- "fieldname": "make_sales_invoice",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Make Sales Invoice",
- "read_only": 1
- },
- {
- "depends_on": "eval: doc.payment_request_type == 'Inward' || doc.payment_channel != \"Phone\"",
- "fieldname": "section_break_10",
- "fieldtype": "Section Break"
- },
- {
- "depends_on": "eval: doc.payment_channel != \"Phone\"",
- "fieldname": "message",
- "fieldtype": "Text",
- "label": "Message"
- },
- {
- "depends_on": "eval: doc.payment_channel != \"Phone\"",
- "fieldname": "message_examples",
- "fieldtype": "HTML",
- "label": "Message Examples",
- "options": "Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n",
- "print_hide": 1
- },
- {
- "default": "0",
- "fieldname": "mute_email",
- "fieldtype": "Check",
- "hidden": 1,
- "label": "Mute Email",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
- "fieldname": "payment_url",
- "fieldtype": "Data",
- "hidden": 1,
- "length": 500,
- "options": "URL",
- "read_only": 1
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "doc.payment_gateway_account",
- "depends_on": "eval: doc.payment_request_type == 'Inward'",
- "fieldname": "section_break_7",
- "fieldtype": "Section Break",
- "label": "Payment Gateway Details"
- },
- {
- "fetch_from": "payment_gateway_account.payment_gateway",
- "fieldname": "payment_gateway",
- "fieldtype": "Read Only",
- "label": "Payment Gateway"
- },
- {
- "fetch_from": "payment_gateway_account.payment_account",
- "fieldname": "payment_account",
- "fieldtype": "Read Only",
- "label": "Payment Account",
- "read_only": 1
- },
- {
- "fetch_from": "payment_gateway_account.payment_channel",
- "fieldname": "payment_channel",
- "fieldtype": "Select",
- "label": "Payment Channel",
- "options": "\nEmail\nPhone",
- "read_only": 1
- },
- {
- "fieldname": "payment_order",
- "fieldtype": "Link",
- "label": "Payment Order",
- "options": "Payment Order",
- "read_only": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Payment Request",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "depends_on": "eval: doc.docstatus === 1",
- "description": "Amount in party's bank account currency",
- "fieldname": "outstanding_amount",
- "fieldtype": "Currency",
- "in_preview": 1,
- "label": "Outstanding Amount",
- "non_negative": 1,
- "options": "party_account_currency",
- "read_only": 1
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "read_only": 1
- },
- {
- "fieldname": "party_account_currency",
- "fieldtype": "Link",
- "label": "Party Account Currency",
- "options": "Currency",
- "read_only": 1
- },
- {
- "fieldname": "party_name",
- "fieldtype": "Data",
- "label": "Party Name",
- "read_only": 1
- }
- ],
- "in_create": 1,
- "index_web_pages_for_search": 1,
- "is_submittable": 1,
- "links": [],
- "modified": "2024-10-23 12:23:40.117336",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Payment Request",
- "naming_rule": "By \"Naming Series\" field",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "share": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts Manager",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "show_preview_popup": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "states": []
-}
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2015-12-15 22:23:24.745065",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "payment_request_type",
+ "transaction_date",
+ "is_payment_order_required",
+ "column_break_2",
+ "naming_series",
+ "company",
+ "mode_of_payment",
+ "party_details",
+ "party_type",
+ "party",
+ "party_name",
+ "column_break_4",
+ "reference_doctype",
+ "reference_name",
+ "transaction_details",
+ "grand_total",
+ "currency",
+ "is_a_subscription",
+ "column_break_18",
+ "outstanding_amount",
+ "party_account_currency",
+ "subscription_section",
+ "subscription_plans",
+ "bank_account_details",
+ "bank_account",
+ "bank",
+ "column_break_11",
+ "iban",
+ "branch_code",
+ "swift_number",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "recipient_and_message",
+ "print_format",
+ "email_to",
+ "subject",
+ "column_break_9",
+ "payment_gateway_account",
+ "status",
+ "make_sales_invoice",
+ "section_break_10",
+ "message",
+ "message_examples",
+ "mute_email",
+ "payment_url",
+ "section_break_7",
+ "payment_gateway",
+ "payment_account",
+ "payment_channel",
+ "payment_order",
+ "amended_from",
+ "bank_account_no",
+ "account"
+ ],
+ "fields": [
+ {
+ "default": "Inward",
+ "fieldname": "payment_request_type",
+ "fieldtype": "Select",
+ "label": "Payment Request Type",
+ "options": "Outward\nInward",
+ "reqd": 1
+ },
+ {
+ "fieldname": "transaction_date",
+ "fieldtype": "Date",
+ "in_preview": 1,
+ "label": "Transaction Date"
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Series",
+ "no_copy": 1,
+ "options": "ACC-PRQ-.YYYY.-",
+ "print_hide": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "label": "Mode of Payment",
+ "options": "Mode of Payment"
+ },
+ {
+ "fieldname": "party_details",
+ "fieldtype": "Section Break",
+ "label": "Party Details"
+ },
+ {
+ "fieldname": "party_type",
+ "fieldtype": "Link",
+ "label": "Party Type",
+ "options": "DocType"
+ },
+ {
+ "fieldname": "party",
+ "fieldtype": "Dynamic Link",
+ "label": "Party",
+ "options": "party_type"
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "in_standard_filter": 1,
+ "label": "Reference Doctype",
+ "no_copy": 1,
+ "options": "DocType",
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1
+ },
+ {
+ "fieldname": "reference_name",
+ "fieldtype": "Dynamic Link",
+ "in_global_search": 1,
+ "in_standard_filter": 1,
+ "label": "Reference Name",
+ "no_copy": 1,
+ "options": "reference_doctype",
+ "print_hide": 1,
+ "read_only": 1,
+ "search_index": 1
+ },
+ {
+ "fieldname": "transaction_details",
+ "fieldtype": "Section Break",
+ "label": "Transaction Details"
+ },
+ {
+ "description": "Amount in transaction currency",
+ "fieldname": "grand_total",
+ "fieldtype": "Currency",
+ "in_preview": 1,
+ "label": "Amount",
+ "non_negative": 1,
+ "options": "currency",
+ "reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_a_subscription",
+ "fieldtype": "Check",
+ "label": "Is a Subscription"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "currency",
+ "fieldtype": "Link",
+ "label": "Transaction Currency",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval:doc.is_a_subscription",
+ "fieldname": "subscription_section",
+ "fieldtype": "Section Break",
+ "label": "Subscription Section"
+ },
+ {
+ "fieldname": "subscription_plans",
+ "fieldtype": "Table",
+ "label": "Subscription Plans",
+ "options": "Subscription Plan Detail"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "bank_account_details",
+ "fieldtype": "Section Break",
+ "label": "Bank Account Details"
+ },
+ {
+ "fieldname": "bank_account",
+ "fieldtype": "Link",
+ "label": "Bank Account",
+ "options": "Bank Account"
+ },
+ {
+ "fetch_from": "bank_account.bank",
+ "fieldname": "bank",
+ "fieldtype": "Link",
+ "label": "Bank",
+ "options": "Bank",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "bank_account.bank_account_no",
+ "fieldname": "bank_account_no",
+ "fieldtype": "Read Only",
+ "label": "Bank Account No"
+ },
+ {
+ "fetch_from": "bank_account.account",
+ "fieldname": "account",
+ "fieldtype": "Read Only",
+ "label": "Account"
+ },
+ {
+ "fieldname": "column_break_11",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "bank_account.iban",
+ "fieldname": "iban",
+ "fieldtype": "Read Only",
+ "label": "IBAN"
+ },
+ {
+ "fetch_from": "bank_account.branch_code",
+ "fetch_if_empty": 1,
+ "fieldname": "branch_code",
+ "fieldtype": "Read Only",
+ "label": "Branch Code"
+ },
+ {
+ "fetch_from": "bank.swift_number",
+ "fieldname": "swift_number",
+ "fieldtype": "Read Only",
+ "label": "SWIFT Number"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.payment_request_type == 'Inward'",
+ "fieldname": "recipient_and_message",
+ "fieldtype": "Section Break",
+ "label": "Recipient Message And Payment Details"
+ },
+ {
+ "depends_on": "eval: doc.payment_channel != \"Phone\"",
+ "fieldname": "print_format",
+ "fieldtype": "Select",
+ "label": "Print Format"
+ },
+ {
+ "fieldname": "email_to",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "To"
+ },
+ {
+ "depends_on": "eval: doc.payment_channel != \"Phone\"",
+ "fieldname": "subject",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Subject"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.payment_request_type == 'Inward'",
+ "fieldname": "payment_gateway_account",
+ "fieldtype": "Link",
+ "label": "Payment Gateway Account",
+ "options": "Payment Gateway Account"
+ },
+ {
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "options": "\nDraft\nRequested\nInitiated\nPartially Paid\nPayment Ordered\nPaid\nFailed\nCancelled",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.reference_doctype==\"Sales Order\"",
+ "fieldname": "make_sales_invoice",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Make Sales Invoice",
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.payment_request_type == 'Inward' || doc.payment_channel != \"Phone\"",
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break"
+ },
+ {
+ "depends_on": "eval: doc.payment_channel != \"Phone\"",
+ "fieldname": "message",
+ "fieldtype": "Text",
+ "label": "Message"
+ },
+ {
+ "depends_on": "eval: doc.payment_channel != \"Phone\"",
+ "fieldname": "message_examples",
+ "fieldtype": "HTML",
+ "label": "Message Examples",
+ "options": "Message Example
\n\n<p>Dear {{ doc.contact_person }},</p>\n\n<p>Requesting payment for {{ doc.doctype }}, {{ doc.name }} for {{ doc.grand_total }}.</p>\n\n<a href=\"{{ payment_url }}\"> click here to pay </a>\n\n
\n",
+ "print_hide": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "mute_email",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Mute Email",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1
+ },
+ {
+ "fieldname": "payment_url",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "length": 500,
+ "options": "URL",
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "doc.payment_gateway_account",
+ "depends_on": "eval: doc.payment_request_type == 'Inward'",
+ "fieldname": "section_break_7",
+ "fieldtype": "Section Break",
+ "label": "Payment Gateway Details"
+ },
+ {
+ "fetch_from": "payment_gateway_account.payment_gateway",
+ "fieldname": "payment_gateway",
+ "fieldtype": "Read Only",
+ "label": "Payment Gateway"
+ },
+ {
+ "fetch_from": "payment_gateway_account.payment_account",
+ "fieldname": "payment_account",
+ "fieldtype": "Read Only",
+ "label": "Payment Account",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "payment_gateway_account.payment_channel",
+ "fieldname": "payment_channel",
+ "fieldtype": "Select",
+ "label": "Payment Channel",
+ "options": "\nEmail\nPhone",
+ "read_only": 1
+ },
+ {
+ "fieldname": "payment_order",
+ "fieldtype": "Link",
+ "label": "Payment Order",
+ "options": "Payment Order",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Payment Request",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.docstatus === 1",
+ "description": "Amount in party's bank account currency",
+ "fieldname": "outstanding_amount",
+ "fieldtype": "Currency",
+ "in_preview": 1,
+ "label": "Outstanding Amount",
+ "non_negative": 1,
+ "options": "party_account_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1
+ },
+ {
+ "fieldname": "party_account_currency",
+ "fieldtype": "Link",
+ "label": "Party Account Currency",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "party_name",
+ "fieldtype": "Data",
+ "label": "Party Name",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_payment_order_required",
+ "fieldtype": "Check",
+ "label": "Is Payment Order Required"
+ }
+ ],
+ "in_create": 1,
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2025-01-14 17:29:21.046587",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payment Request",
+ "naming_rule": "By \"Naming Series\" field",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "show_preview_popup": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index e77c75ff11be..d094e482db2e 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -35,12 +35,9 @@ class PaymentRequest(Document):
from typing import TYPE_CHECKING
if TYPE_CHECKING:
+ from erpnext.accounts.doctype.subscription_plan_detail.subscription_plan_detail import SubscriptionPlanDetail
from frappe.types import DF
- from erpnext.accounts.doctype.subscription_plan_detail.subscription_plan_detail import (
- SubscriptionPlanDetail,
- )
-
account: DF.ReadOnly | None
amended_from: DF.Link | None
bank: DF.Link | None
@@ -54,6 +51,7 @@ class PaymentRequest(Document):
grand_total: DF.Currency
iban: DF.ReadOnly | None
is_a_subscription: DF.Check
+ is_payment_order_required: DF.Check
make_sales_invoice: DF.Check
message: DF.Text | None
mode_of_payment: DF.Link | None
@@ -65,7 +63,7 @@ class PaymentRequest(Document):
party_name: DF.Data | None
party_type: DF.Link | None
payment_account: DF.ReadOnly | None
- payment_channel: DF.Literal["", "Email", "Phone", "Other"]
+ payment_channel: DF.Literal["", "Email", "Phone"]
payment_gateway: DF.ReadOnly | None
payment_gateway_account: DF.Link | None
payment_order: DF.Link | None
@@ -74,17 +72,7 @@ class PaymentRequest(Document):
print_format: DF.Literal[None]
reference_doctype: DF.Link | None
reference_name: DF.DynamicLink | None
- status: DF.Literal[
- "",
- "Draft",
- "Requested",
- "Initiated",
- "Partially Paid",
- "Payment Ordered",
- "Paid",
- "Failed",
- "Cancelled",
- ]
+ status: DF.Literal["", "Draft", "Requested", "Initiated", "Partially Paid", "Payment Ordered", "Paid", "Failed", "Cancelled"]
subject: DF.Data | None
subscription_plans: DF.Table[SubscriptionPlanDetail]
swift_number: DF.ReadOnly | None
@@ -211,6 +199,7 @@ def request_phone_payment(self):
sender=self.email_to,
currency=self.currency,
payment_gateway=self.payment_gateway,
+ phone_number=self.phone_number,
)
controller.validate_transaction_currency(self.currency)
@@ -619,6 +608,7 @@ def validate_and_calculate_grand_total(grand_total, existing_payment_request_amo
"party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account,
"party_name": args.get("party_name") or ref_doc.get("customer_name"),
+ "phone_number": args.get("phone_number") if args.get("phone_number") else None,
}
)
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 1dbc630e62ec..b34a71b7d75b 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -6,7 +6,7 @@
import frappe
from frappe import _
-
+from frappe.utils import cint, flt, getdate, today
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -18,7 +18,8 @@
make_serial_batch_bundle,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
-
+from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import make_closing_entry_from_opening
+from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
class TestPOSInvoice(unittest.TestCase):
@classmethod
@@ -924,8 +925,146 @@ def test_delivered_serial_no_case(self):
finally:
frappe.db.rollback(save_point="before_test_delivered_serial_no_case")
frappe.set_user("Administrator")
+
+ def test_pos_opening_to_pos_closing_with_possi_and_tax_TC_S_102(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+
+ test_user, pos_profile = init_user_and_profile()
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+ pos_inv = create_pos_invoice(rate=3500, do_not_submit=1)
+ for i in pos_inv.items:
+ i.item_tax_template = "GST 5% - _TC"
+ pos_inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
+ pos_inv.taxes_and_charges = "Output GST In-state - _TC"
+ pos_inv.save()
+ pos_inv.submit()
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(opening_entry.status, "Closed")
+
+
+ def test_pos_invoice_with_loyalty_point_TC_S_103(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+ from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points
+
+ if not frappe.db.exists("Loyalty Program", "Test Single Loyalty"):
+ frappe.get_doc(
+ {
+ "doctype": "Loyalty Program",
+ "loyalty_program_name": "Test Single Loyalty",
+ "auto_opt_in": 1,
+ "from_date": today(),
+ "loyalty_program_type": "Single Tier Program",
+ "conversion_factor": 1,
+ "expiry_duration": 10,
+ "company": "_Test Company",
+ "cost_center": "Main - _TC",
+ "expense_account": "Loyalty - _TC",
+ "collection_rules": [{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 1000}],
+ }
+ ).insert()
+
+ test_user, pos_profile = init_user_and_profile()
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+ before_lp_details = get_loyalty_program_details_with_points(
+ "Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
+ )
+
+ inv = create_pos_invoice(customer="Test Loyalty Customer", rate=10000, do_not_save=1)
+ inv.redeem_loyalty_points = 1
+ inv.loyalty_points = before_lp_details.loyalty_points
+ inv.loyalty_amount = inv.loyalty_points * before_lp_details.conversion_factor
+ inv.append(
+ "payments",
+ {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 10000 - inv.loyalty_amount},
+ )
+ inv.paid_amount = 10000
+ inv.submit()
+ after_redeem_lp_details = get_loyalty_program_details_with_points(
+ inv.customer, company=inv.company, loyalty_program=inv.loyalty_program
+ )
+ self.assertEqual(after_redeem_lp_details.loyalty_points, 9)
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(inv.status, "Paid")
+ self.assertEqual(opening_entry.status, "Closed")
+
+ def test_pos_inoivce_with_discount_grand_total_TC_S_104(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+
+ test_user, pos_profile = init_user_and_profile()
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+ inv = create_pos_invoice(customer="Test Loyalty Customer", rate=3000, do_not_save=1)
+ inv.taxes_and_charges = "Output GST In-state - _TC"
+ inv.apply_discount_on = "Grand Total"
+ inv.discount_amount = 1000
+
+ inv.save()
+ inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": inv.grand_total})
+ inv.paid_amount = inv.grand_total
+ inv.submit()
+
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+
+ self.assertEqual(inv.status, "Paid")
+ self.assertEqual(opening_entry.status, "Closed")
+
+ def test_pos_inoivce_with_discount_net_total_TC_S_105(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+
+ test_user, pos_profile = init_user_and_profile()
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+ inv = create_pos_invoice(customer="Test Loyalty Customer", rate=3000, do_not_save=1)
+ inv.taxes_and_charges = "Output GST In-state - _TC"
+ inv.apply_discount_on = "Net Total"
+ inv.discount_amount = 1000
+
+ inv.save()
+ inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": inv.grand_total})
+
+ inv.paid_amount = inv.grand_total
+ inv.submit()
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(inv.status, "Paid")
+ self.assertEqual(opening_entry.status, "Closed")
+
+ def test_pos_inoivce_with_terms_and_conditions_TC_S_107(self):
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+
+ test_user, pos_profile = init_user_and_profile()
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+ inv = create_pos_invoice(customer="Test Loyalty Customer", rate=3000, do_not_save=1)
+ inv.taxes_and_charges = "Output GST In-state - _TC"
+ inv.tc_name ="_Test Terms and Conditions"
+ inv.save()
+ inv.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": inv.grand_total})
+
+ inv.paid_amount = inv.grand_total
+ inv.submit()
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(inv.status, "Paid")
+ self.assertEqual(opening_entry.status, "Closed")
+
def create_pos_invoice(**args):
args = frappe._dict(args)
pos_profile = None
diff --git a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
index 64c658ab151b..029821cedf65 100644
--- a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
+++ b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py
@@ -3,12 +3,94 @@
import unittest
+from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import make_closing_entry_from_opening
import frappe
class TestPOSOpeningEntry(unittest.TestCase):
- pass
+ def test_pos_opening_to_pos_closing_TC_S_099(self):
+ from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+ test_user, pos_profile = init_user_and_profile()
+
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+
+
+ pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
+ pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
+ pos_inv1.submit()
+
+ pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
+ pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
+ pos_inv2.submit()
+
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(opening_entry.status, "Closed")
+
+ def test_pos_opening_to_closing_enrty_check_cashire_and_posprofile_TC_S_100(self):
+ from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+ test_user, pos_profile = init_user_and_profile()
+
+ opening_entry = create_opening_entry(pos_profile=pos_profile, user=test_user.name)
+ self.assertEqual(opening_entry.status, "Open")
+ self.assertEqual(opening_entry.pos_profile, pos_profile.name)
+ self.assertEqual(opening_entry.user,test_user.name)
+
+
+ pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
+ pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
+ pos_inv1.submit()
+
+ pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
+ pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
+ pos_inv2.submit()
+
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(opening_entry.status, "Closed")
+
+ def test_pos_opening_to_closing_enrty_with_opening_balance_TC_S_101(self):
+ from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice
+ from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile
+ test_user, pos_profile = init_user_and_profile()
+
+ opening_entry = frappe.new_doc("POS Opening Entry")
+ opening_entry.pos_profile = pos_profile.name
+ opening_entry.user = test_user.name
+ opening_entry.company = pos_profile.company
+ opening_entry.period_start_date = frappe.utils.get_datetime()
+
+ # Add opening balance details
+ balance_details = []
+ for d in pos_profile.payments:
+ balance_details.append({
+ "mode_of_payment": d.mode_of_payment,
+ "opening_amount": 1000
+ })
+
+ opening_entry.set("balance_details", balance_details)
+ opening_entry.save()
+ opening_entry.submit()
+ self.assertEqual(opening_entry.status, "Open")
+
+ pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1)
+ pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500})
+ pos_inv1.submit()
+
+ pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
+ pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
+ pos_inv2.submit()
+
+ closing_enrty= make_closing_entry_from_opening(opening_entry)
+ closing_enrty.submit()
+ opening_entry.reload()
+ self.assertEqual(opening_entry.status, "Closed")
def create_opening_entry(pos_profile, user):
entry = frappe.new_doc("POS Opening Entry")
@@ -24,4 +106,4 @@ def create_opening_entry(pos_profile, user):
entry.set("balance_details", balance_details)
entry.submit()
- return entry.as_dict()
+ return entry
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
index 6ff9bc3b7ffc..35c9c99eb37c 100644
--- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.json
@@ -1,7 +1,6 @@
{
"actions": [],
"autoname": "format:ACC-PPR-{#####}",
- "beta": 1,
"creation": "2023-03-30 21:28:39.793927",
"default_view": "List",
"doctype": "DocType",
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
index 882a2c4ad1cc..35f1e31af349 100644
--- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py
@@ -212,7 +212,7 @@ def trigger_reconciliation_for_queued_docs():
unique_filters = set()
queue_size = 5
- fields = ["company", "party_type", "party", "receivable_payable_account"]
+ fields = ["company", "party_type", "party", "receivable_payable_account", "default_advance_account"]
def get_filters_as_tuple(fields, doc):
filters = ()
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
index b4ac9812cbf2..e46e7223531e 100644
--- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
@@ -1,7 +1,6 @@
{
"actions": [],
"autoname": "format:PPR-LOG-{##}",
- "beta": 1,
"creation": "2023-03-13 15:00:09.149681",
"default_view": "List",
"doctype": "DocType",
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index b3cf02df039f..7793ddbd2e3d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -2490,7 +2490,7 @@ def test_tax_withholding_with_supplier_TC_ACC_023(self):
"against_voucher": None
},
{
- "account": "Test TDS Payable - _TC",
+ "account": "_Test TDS Payable - _TC",
"debit": 0.0,
"credit": 1800.0,
"against_voucher": None
@@ -2602,7 +2602,7 @@ def test_lower_tax_deduction_TC_ACC_025_and_TC_ACC_026(self):
["Creditors - _TC", 0.0, 50000.0, pi.posting_date],
["Creditors - _TC", 600.0, 0.0, pi.posting_date],
["Stock Received But Not Billed - _TC", 50000.0, 0.0, pi.posting_date],
- ["Test TDS Payable - _TC", 0.0, 600.0, pi.posting_date]
+ ["_Test TDS Payable - _TC", 0.0, 600.0, pi.posting_date]
]
check_gl_entries(self, pi.name, expected_gle, pi.posting_date)
@@ -2632,6 +2632,8 @@ def test_currency_exchange_with_pi_TC_ACC_027(self):
pi.submit()
pe = get_payment_entry("Purchase Invoice", pi.name)
+ pe.payment_type= "Pay"
+ pe.paid_from = "Cash - _TC"
pe.target_exchange_rate = 60
pe.save()
pe.submit()
@@ -2779,6 +2781,8 @@ def test_advance_payment_TC_ACC_028(self):
_pe = get_payment_entry('Purchase Invoice', pi.name)
_pe.target_exchange_rate = 62
+ _pe.payment_type= "Pay"
+ _pe.paid_from = "Cash - _TC"
_pe.save()
_pe.submit()
@@ -2835,7 +2839,9 @@ def test_single_payment_request_for_purchase_invoice_TC_ACC_035(self):
submit_doc=1,
return_doc=1,
)
- pe=pr.create_payment_entry()
+ pe=pr.create_payment_entry(submit=False)
+ pe.payment_type= "Pay"
+ pe.paid_from = "Cash - _TC"
pe.save()
pe.submit()
pr.load_from_db()
@@ -2887,7 +2893,9 @@ def test_multi_payment_request_for_purchase_invoice_TC_ACC_036(self):
pr.grand_total = pr.grand_total / 2
pr.save()
pr.submit()
- pe=pr.create_payment_entry()
+ pe=pr.create_payment_entry(submit=False)
+ pe.payment_type= "Pay"
+ pe.paid_from = "Cash - _TC"
pe.save()
pe.submit()
pr.load_from_db()
@@ -2914,7 +2922,9 @@ def test_multi_payment_request_for_purchase_invoice_TC_ACC_036(self):
return_doc=1,
submit_doc=1,
)
- _pe=_pr.create_payment_entry()
+ _pe=_pr.create_payment_entry(submit=False)
+ _pe.payment_type= "Pay"
+ _pe.paid_from = "Cash - _TC"
_pe.save()
_pe.submit()
_pr.load_from_db()
@@ -2977,6 +2987,22 @@ def test_invoice_status_on_payment_entry_submit_TC_B_035_and_TC_B_037(self):
pi_status_after_reconcile = frappe.db.get_value("Purchase Invoice", pi.name, "status")
self.assertEqual(pi_status_after_reconcile, "Unpaid")
+ new_pi = make_purchase_invoice(
+ qty=1,
+ item_code="_Test Item",
+ supplier = "_Test Supplier",
+ company = "_Test Company",
+ rate = 30,
+ do_not_save =True,
+ )
+ new_pi.save()
+ new_pi.set_advances()
+ new_pi.save()
+ new_pi.submit()
+
+ pi_status_after_advances = frappe.db.get_value("Purchase Invoice", new_pi.name, "status")
+ self.assertEqual(pi_status_after_advances, "Paid")
+
def test_partly_paid_of_pi_to_pr_to_pe_TC_B_081(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
pi = make_purchase_invoice(
@@ -3176,7 +3202,85 @@ def test_fully_paid_of_pi_to_pr_to_pe_with_gst_TC_B_084(self):
pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
self.assertEqual(pi_status, "Paid")
+
+ def test_standalone_pi_is_fully_paid_TC_B_088(self):
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+
+ purchase_tax.save()
+
+ pi = make_purchase_invoice(
+ qty=1,
+ item_code="_Test Item",
+ supplier = "_Test Supplier",
+ company = "_Test Company",
+ rate = 500,
+ do_not_save = True
+ )
+
+
+ pi.taxes_and_charges = purchase_tax.name
+ pi.is_paid = 1
+ pi.mode_of_payment = "Cash"
+ pi.cash_bank_account = "Cash - _TC"
+ pi.save()
+ pi.paid_amount = pi.grand_total
+ pi.submit()
+ pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
+ self.assertEqual(pi_status, "Paid")
+
+ def test_standalone_pi_is_partly_paid_TC_B_090(self):
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+
+ purchase_tax.save()
+
+ pi = make_purchase_invoice(
+ qty=1,
+ item_code="_Test Item",
+ supplier = "_Test Supplier",
+ company = "_Test Company",
+ rate = 500,
+ do_not_save = True
+ )
+
+
+ pi.taxes_and_charges = purchase_tax.name
+ pi.is_paid = 1
+ pi.mode_of_payment = "Cash"
+ pi.cash_bank_account = "Cash - _TC"
+ pi.save()
+ pi.paid_amount = pi.grand_total / 2
+ pi.submit()
+
+ pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
+ self.assertEqual(pi_status, "Partly Paid")
+
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(
"Company",
@@ -3213,7 +3317,6 @@ def check_gl_entries(
for col in additional_columns:
query = query.select(gl[col])
gl_entries = query.run(as_dict=True)
-
for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
@@ -3449,6 +3552,7 @@ def update_ldc_details(supplier):
setattr(supplier,'custom_lower_tds_deduction_applicable','Yes')
if not supplier.pan:
setattr(supplier,'pan','DAJPC4150P')
+ supplier.flags.ignore_mandatory = True
supplier.save()
frappe.db.commit()
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index c168cac5675e..0982e52a0d64 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -4263,15 +4263,15 @@ def test_jv_records_creation_diff_ex_rate_TC_ACC_029(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.payment_entry.test_payment_entry import make_test_item
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import get_jv_entry_account
-
+
create_cost_center(
- cost_center_name="_Test Cost Center - _TC",
+ cost_center_name="_Test Cost Center",
company="_Test Company",
parent_cost_center="_Test Company - _TC"
)
create_account(
- account_name="_Test Receivable USD - _TC",
+ account_name="_Test Receivable USD",
parent_account="Current Assets - _TC",
company="_Test Company",
account_currency="USD",
@@ -4315,7 +4315,11 @@ def test_jv_records_creation_diff_ex_rate_TC_ACC_029(self):
si.save()
si.submit()
- pe = get_payment_entry("Sales Invoice", si.name)
+ pe = get_payment_entry("Sales Invoice", si.name)
+ pe.payment_type == "Receive"
+ pe.mode_of_payment = "Cash"
+ pe.paid_from="_Test Receivable USD - _TC"
+ pe.paid_to = "Cash - _TC"
pe.source_exchange_rate = 60
pe.save()
pe.submit()
@@ -4352,20 +4356,20 @@ def test_jv_records_creation_diff_ex_rate_TC_ACC_030(self):
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import get_jv_entry_account
create_cost_center(
- cost_center_name="_Test Cost Center - _TC",
+ cost_center_name="_Test Cost Center",
company="_Test Company",
parent_cost_center="_Test Company - _TC"
)
create_account(
- account_name="_Test Receivable USD - _TC",
+ account_name="_Test Receivable USD",
parent_account="Current Assets - _TC",
company="_Test Company",
account_currency="USD",
account_type="Receivable",
)
create_account(
- account_name="_Test Cash - _TC",
+ account_name="_Test Cash",
parent_account="Cash In Hand - _TC",
company="_Test Company",
account_currency="INR",
@@ -4461,8 +4465,221 @@ def test_jv_records_creation_diff_ex_rate_TC_ACC_030(self):
voucher_type="Journal Entry"
)
+ def test_single_payment_request_for_purchase_invoice_TC_ACC_037(self):
+ from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+ from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import make_test_item
+
+ setup_bank_accounts()
+
+ create_cost_center(
+ cost_center_name="_Test Cost Center",
+ company="_Test Company",
+ parent_cost_center="_Test Company - _TC"
+ )
+
+ create_account(
+ account_name="_Test Receivable",
+ parent_account="Current Assets - _TC",
+ company="_Test Company",
+ account_currency="INR",
+ account_type="Receivable",
+ )
+ create_account(
+ account_name="_Test Cash",
+ parent_account="Cash In Hand - _TC",
+ company="_Test Company",
+ account_currency="INR",
+ account_type="Cash",
+ )
+
+ create_customer(
+ customer_name="_Test Customer",
+ currency="INR",
+ company="_Test Company",
+ account="_Test Receivable - _TC"
+ )
+
+ item = make_test_item(item_name="_Test Item")
+
+ if item.is_new():
+ item.append(
+ "item_defaults",
+ {
+ "default_warehouse": '_Test Warehouse - _TC',
+ "company": "_Test Company",
+ "selling_cost_center": "_Test Cost Center - _TC",
+ },
+ )
+ item.save()
+
+ frappe.db.commit()
+
+ customer =frappe.get_doc("Customer", "_Test Customer")
+
+ if customer:
+ si=create_sales_invoice(
+ customer="_Test Customer",
+ company="_Test Company",
+ parent_cost_center="_Test Cost Center - _TC",
+ item_code=item.name,
+ qty=1,
+ rate=5000,
+ )
+
+ si.submit()
+
+ pr =make_payment_request(
+ dt="Sales Invoice",
+ dn=si.name,
+ recipient_id="test@test.com",
+ payment_gateway_account="_Test Gateway - INR",
+ mute_email=1,
+ submit_doc=1,
+ return_doc=1,
+ )
+
+ pe = pr.set_as_paid()
+ pe.save()
+ pe.submit()
+ pr.load_from_db()
+ self.assertEqual(pr.status, "Paid")
+ si.load_from_db()
+ self.assertEqual(si.status, "Paid")
+ expected_gle = [
+ ["Debtors - _TC", 0.0, si.grand_total, pe.posting_date],
+ ["_Test Bank - _TC", si.grand_total, 0.0, pe.posting_date]
+ ]
+ check_gl_entries(
+ doc=self,
+ voucher_no=pe.name,
+ expected_gle=expected_gle,
+ voucher_type="Payment Entry",
+ posting_date=pe.posting_date
+ )
+
+ def test_multiple_payment_request_for_purchase_invoice_TC_ACC_038(self):
+ from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+ from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import make_test_item
+
+ setup_bank_accounts()
+
+ create_cost_center(
+ cost_center_name="_Test Cost Center",
+ company="_Test Company",
+ parent_cost_center="_Test Company - _TC"
+ )
+
+ create_account(
+ account_name="_Test Receivable",
+ parent_account="Current Assets - _TC",
+ company="_Test Company",
+ account_currency="INR",
+ account_type="Receivable",
+ )
+ create_account(
+ account_name="_Test Cash",
+ parent_account="Cash In Hand - _TC",
+ company="_Test Company",
+ account_currency="INR",
+ account_type="Cash",
+ )
+
+ create_customer(
+ customer_name="_Test Customer",
+ currency="INR",
+ company="_Test Company",
+ account="_Test Receivable - _TC"
+ )
+
+ item = make_test_item(item_name="_Test Item")
+
+ if item.is_new():
+ item.append(
+ "item_defaults",
+ {
+ "default_warehouse": '_Test Warehouse - _TC',
+ "company": "_Test Company",
+ "selling_cost_center": "_Test Cost Center - _TC",
+ },
+ )
+ item.save()
+ frappe.db.commit()
+
+ customer =frappe.get_doc("Customer", "_Test Customer")
+
+ if customer:
+ si=create_sales_invoice(
+ customer="_Test Customer",
+ company="_Test Company",
+ parent_cost_center="_Test Cost Center - _TC",
+ item_code=item.name,
+ qty=1,
+ rate=5000,
+ )
+ si.submit()
+
+ pr =make_payment_request(
+ dt="Sales Invoice",
+ dn=si.name,
+ recipient_id="test@test.com",
+ payment_gateway_account="_Test Gateway - INR",
+ mute_email=1,
+ return_doc=1,
+ )
+ pr.grand_total = pr.grand_total / 2
+ pr.save()
+ pr.submit()
+ pe = pr.set_as_paid()
+ pe.save()
+ pe.submit()
+
+ pr.load_from_db()
+ self.assertEqual(pr.status, "Paid")
+ si.load_from_db()
+ self.assertEqual(si.status, "Partly Paid")
+ expected_gle = [
+ ["Debtors - _TC", 0.0, si.grand_total/2, pe.posting_date],
+ ["_Test Bank - _TC", si.grand_total/2, 0.0, pe.posting_date]
+ ]
+ check_gl_entries(
+ doc=self,
+ voucher_no=pe.name,
+ expected_gle=expected_gle,
+ voucher_type="Payment Entry",
+ posting_date=pe.posting_date
+ )
+ _pr=make_payment_request(
+ dt="Sales Invoice",
+ dn=si.name,
+ recipient_id="test@test.com",
+ payment_gateway_account="_Test Gateway - INR",
+ mute_email=1,
+ return_doc=1,
+ submit_doc=1,
+ )
+ _pe=_pr.set_as_paid()
+ _pe.save()
+ _pe.submit()
+
+ _pr.load_from_db()
+ self.assertEqual(_pr.status, "Paid")
+ si.load_from_db()
+ self.assertEqual(si.status, "Paid")
+ expected_gle = [
+ ["Debtors - _TC", 0.0, si.grand_total/2, _pe.posting_date],
+ ["_Test Bank - _TC", si.grand_total/2, 0.0, _pe.posting_date]
+ ]
+ check_gl_entries(
+ doc=self,
+ voucher_no=_pe.name,
+ expected_gle=expected_gle,
+ voucher_type="Payment Entry",
+ posting_date=pe.posting_date
+ )
def test_sales_invoice_without_sales_order_with_gst_TC_S_016(self):
setting = frappe.get_doc("Selling Settings")
setting.so_required = 'No'
@@ -5077,7 +5294,7 @@ def get_taxes_and_charges():
def create_internal_parties():
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
-
+
create_internal_customer(
customer_name="_Test Internal Customer",
represents_company="_Test Company 1",
@@ -5196,5 +5413,32 @@ def create_accounts(**args):
frappe.log_error(f"Failed to insert {args.get('account_name')}", str(e))
+def setup_bank_accounts():
+ payment_gateway = {"doctype": "Payment Gateway", "gateway": "_Test Gateway"}
-
+ payment_method = [
+ {
+ "doctype": "Payment Gateway Account",
+ "is_default": 1,
+ "payment_gateway": "_Test Gateway",
+ "payment_account": "_Test Bank - _TC",
+ "currency": "INR",
+ },
+ {
+ "doctype": "Payment Gateway Account",
+ "payment_gateway": "_Test Gateway",
+ "payment_account": "_Test Bank USD - _TC",
+ "currency": "USD",
+ },
+ ]
+
+ if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"):
+ frappe.get_doc(payment_gateway).insert(ignore_permissions=True)
+
+ for method in payment_method:
+ if not frappe.db.get_value(
+ "Payment Gateway Account",
+ {"payment_gateway": method["payment_gateway"], "currency": method["currency"]},
+ "name",
+ ):
+ frappe.get_doc(method).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index 58ac76b83abc..f90d92996a74 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -1091,7 +1091,14 @@ def create_tax_withholding_category(
"accounts": [{"company": "_Test Company", "account": account}],
}
).insert()
-
+ elif frappe.db.exists("Tax Withholding Category", category_name):
+ doc = frappe.get_doc("Tax Withholding Category",category_name)
+ if doc.accounts:
+ child=frappe.get_doc("Tax Withholding Account",doc.accounts[0].name)
+ if child.account != account:
+ child.account=account
+ child.save()
+ frappe.db.commit()
def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_rate, certificate_no, limit):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
index e0be8e575b81..eec1951c6a9e 100644
--- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
+++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.py
@@ -142,7 +142,8 @@ def get_journal_entries(filters):
where jvd.parent = jv.name and jv.docstatus=1
and jvd.account = %(account)s and jv.posting_date <= %(report_date)s
and ifnull(jv.clearance_date, '4000-01-01') > %(report_date)s
- and ifnull(jv.is_opening, 'No') = 'No'""",
+ and ifnull(jv.is_opening, 'No') = 'No'
+ and jv.company = %(company)s """,
filters,
as_dict=1,
)
@@ -168,6 +169,7 @@ def get_payment_entries(filters):
AND docstatus = 1
AND posting_date <= %(report_date)s
AND COALESCE(clearance_date, '4000-01-01') > %(report_date)s
+ and company = %(company)s
""",
filters,
as_dict=1,
@@ -186,6 +188,7 @@ def get_pos_entries(filters):
sip.account=%(account)s and si.docstatus=1 and sip.parent = si.name
and account.name = sip.account and si.posting_date <= %(report_date)s and
ifnull(sip.clearance_date, '4000-01-01') > %(report_date)s
+ and si.company = %(company)s
order by
si.posting_date ASC, si.name DESC
""",
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index e540aa9993c8..dec189da4185 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -240,8 +240,8 @@ def get_actual_details(name, filters):
if filters.get("budget_against") == "Cost Center":
cc_lft, cc_rgt = frappe.db.get_value("Cost Center", name, ["lft", "rgt"])
cond = f"""
- and lft >= "{cc_lft}"
- and rgt <= "{cc_rgt}"
+ and lft >= {cc_lft}
+ and rgt <= {cc_rgt}
"""
ac_details = frappe.db.sql(
@@ -251,8 +251,8 @@ def get_actual_details(name, filters):
gl.debit,
gl.credit,
gl.fiscal_year,
- MONTHNAME(gl.posting_date) as month_name,
- b.{budget_against} as budget_against
+ TO_CHAR(gl.posting_date, 'Month') AS month_name,
+ (ARRAY_AGG(b.{budget_against}))[1] as budget_against
from
`tabGL Entry` gl,
`tabBudget Account` ba,
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 0a55ae273a43..1a6bcb3bd633 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -810,13 +810,25 @@ def get_group_by_and_display_fields(filters):
def add_sub_total_row(item, total_row_map, group_by_value, tax_columns):
- total_row = total_row_map.get(group_by_value)
- total_row["stock_qty"] += item["stock_qty"]
- total_row["amount"] += item["amount"]
- total_row["total_tax"] += item["total_tax"]
- total_row["total"] += item["total"]
- total_row["percent_gt"] += item["percent_gt"]
-
- for tax in tax_columns:
- total_row.setdefault(frappe.scrub(tax + " Amount"), 0.0)
- total_row[frappe.scrub(tax + " Amount")] += flt(item[frappe.scrub(tax + " Amount")])
+ if group_by_value not in total_row_map:
+ total_row_map[group_by_value] = {
+ "stock_qty": 0.0,
+ "amount": 0.0,
+ "total_tax": 0.0,
+ "total": 0.0,
+ "percent_gt": 0.0
+ }
+
+ total_row = total_row_map.get(group_by_value)
+
+ total_row["stock_qty"] += item.get("stock_qty", 0)
+ total_row["amount"] += item.get("amount", 0)
+ total_row["total_tax"] += item.get("total_tax", 0)
+ total_row["total"] += item.get("total", 0)
+ total_row["percent_gt"] += item.get("percent_gt", 0)
+
+ for tax in tax_columns:
+ tax_amount_field = frappe.scrub(tax + " Amount")
+ total_row.setdefault(tax_amount_field, 0.0)
+ total_row[tax_amount_field] += item.get(tax_amount_field, 0.0)
+
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 8e7370e64738..9f3db7435c2f 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -4,6 +4,7 @@
import frappe
from frappe import _
+from frappe.boot import DocType
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, cstr, flt, formatdate, getdate
@@ -143,13 +144,23 @@ def get_rootwise_opening_balances(filters, report_type):
)
if not ignore_closing_balances:
- last_period_closing_voucher = frappe.db.get_all(
- "Period Closing Voucher",
- filters={"docstatus": 1, "company": filters.company, "period_end_date": ("<", filters.from_date)},
- fields=["period_end_date", "name"],
- order_by="period_end_date desc",
- limit=1,
+ PeriodClosingVoucher = DocType('Period Closing Voucher')
+
+ query = (
+ frappe.qb.from_(PeriodClosingVoucher)
+ .select(
+ PeriodClosingVoucher.period_end_date,
+ PeriodClosingVoucher.name
+ )
+ .where(
+ (PeriodClosingVoucher.docstatus == 1) &
+ (PeriodClosingVoucher.company == filters.company) &
+ (PeriodClosingVoucher.period_end_date < filters.from_date)
+ )
+ .orderby(PeriodClosingVoucher.period_end_date, order=frappe.qb.desc)
+ .limit(1)
)
+ last_period_closing_voucher = query.run()
accounting_dimensions = get_accounting_dimensions(as_list=False)
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 6cbe6c5d0efc..4ce25aaffa72 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -1444,10 +1444,10 @@ def test_pi_return_TC_B_043(self):
check_gl_entries(self, pi.name, expected_gle, nowdate())
actual_qty_1 = get_qty_after_transaction(warehouse="Finished Goods - _TC")
self.assertEqual(actual_qty_0 + 1, actual_qty_1)
-
+
po_status = frappe.db.get_value("Purchase Order", po.name, "status")
self.assertEqual(po_status, "Completed")
-
+
pi_return = make_debit_note(pi.name)
pi_return.update_outstanding_for_self = 0
pi_return.update_billed_amount_in_purchase_receipt = 0
@@ -1462,27 +1462,27 @@ def test_pi_return_TC_B_043(self):
check_gl_entries(self, pi_return.name, expected_gle, nowdate())
actual_qty_2 = get_qty_after_transaction(warehouse="Finished Goods - _TC")
self.assertEqual(actual_qty_1 - 1, actual_qty_2)
-
+
pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
self.assertEqual(pi_status, "Debit Note Issued")
def test_payment_entry_TC_B_037(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import check_gl_entries
-
+
po = create_purchase_order(
warehouse="Finished Goods - _TC",
rate=30,
qty=1,
)
-
+
self.assertEqual(po.status, "To Receive and Bill")
pi = make_pi_from_po(po.name)
pi.update_stock = 1
pi.save()
pi.submit()
pi.load_from_db()
-
+
expected_gle = [
["Creditors - _TC", 0.0, 30, nowdate()],
["_Test Account Cost for Goods Sold - _TC", 30, 0.0, nowdate()],
@@ -1499,17 +1499,17 @@ def test_payment_entry_TC_B_037(self):
{"account": "Cash - _TC", "debit": 0.0, "credit": 30.0},
]
check_payment_gl_entries(self, pe.name, expected_gle)
-
+
def test_purchase_invoice_cancellation_TC_B_041(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
-
+
po = create_purchase_order(
warehouse="Finished Goods - _TC",
rate=130,
qty=1,
)
self.assertEqual(po.status, "To Receive and Bill")
-
+
pr = make_purchase_receipt(po.name)
pr.save()
pr.submit()
@@ -1532,7 +1532,6 @@ def test_purchase_invoice_cancellation_TC_B_041(self):
self.assertEqual(po_status, "To Bill")
pr_status = frappe.db.get_value("Purchase Receipt", pr.name, "status")
self.assertEqual(pr_status, "To Bill")
-
def test_purchase_invoice_return_TC_B_042(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_debit_note
@@ -1558,11 +1557,11 @@ def test_purchase_invoice_return_TC_B_042(self):
self.assertEqual(pi_return.status, "Return")
pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
self.assertEqual(pi_status, "Debit Note Issued")
-
+
def test_50_50_payment_terms_TC_B_045(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
-
+
po = create_purchase_order(
warehouse="Finished Goods - _TC",
rate=130,
@@ -1572,7 +1571,7 @@ def test_50_50_payment_terms_TC_B_045(self):
po.payment_terms_template = "_Test Payment Term Template"
po.save()
po.submit()
-
+
pe = get_payment_entry("Purchase Order", po.name, party_amount=po.grand_total/2)
pe.save()
pe.submit()
@@ -1595,7 +1594,7 @@ def test_50_50_payment_terms_TC_B_045(self):
pe.submit()
po_status = frappe.db.get_value("Purchase Order", po.name, "status")
self.assertEqual(po_status, "Completed")
-
+
pi_status = frappe.db.get_value("Purchase Invoice", pi.name, "status")
self.assertEqual(pi_status, "Paid")
@@ -1960,6 +1959,221 @@ def test_pr_to_lcv_add_value_to_stock_TC_B_034(self):
)
self.assertGreater(len(gl_entries), 0)
+ def test_po_to_pr_with_gst_partly_paid_TC_B_085(self):
+ # Scenario : PO => PR with GST Partly Paid
+
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+ purchase_tax.save()
+ po = create_purchase_order(do_not_submit=True)
+ po.taxes_and_charges = purchase_tax.name
+ po.save()
+ po.submit()
+ self.assertEqual(po.docstatus,1)
+
+ args = {
+ "dt": po.doctype,
+ "dn": po.name,
+ "payment_request_type": 'Outward',
+ "party_type": "Supplier",
+ "party": po.supplier,
+ "party_name": po.supplier_name
+ }
+ partly_pr = make_payment_request(**args)
+ doc_pr = frappe.get_doc("Payment Request", partly_pr.name)
+ # set half amount to be paid
+ doc_pr.grand_total = po.grand_total / 2
+ doc_pr.submit()
+ po_status = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status,'To Receive and Bill')
+
+ def test_po_to_pr_with_gst_fully_paid_TC_B_086(self):
+ # Scenario : PO => PR with GST Fully Paid
+
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+ purchase_tax.save()
+ po = create_purchase_order(do_not_submit=True)
+ po.taxes_and_charges = purchase_tax.name
+ po.save()
+ po.submit()
+ self.assertEqual(po.docstatus,1)
+
+ args = {
+ "dt": po.doctype,
+ "dn": po.name,
+ "payment_request_type": 'Outward',
+ "party_type": "Supplier",
+ "party": po.supplier,
+ "party_name": po.supplier_name
+ }
+ partly_pr = make_payment_request(**args)
+ doc_pr = frappe.get_doc("Payment Request", partly_pr.name)
+ doc_pr.grand_total = po.grand_total
+ doc_pr.submit()
+ po_status = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status,'To Receive and Bill')
+
+ def test_po_to_pr_to_pi_fully_paid_TC_B_087(self):
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+
+ purchase_tax.save()
+
+ po = create_purchase_order(do_not_save=True)
+ po.taxes_and_charges = purchase_tax.name
+ po.save()
+ po.submit()
+ po_status_before = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_before,'To Receive and Bill')
+
+ pr = make_purchase_receipt(po.name)
+ pr.save()
+ pr.submit()
+
+ po_status_after_pr = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_after_pr,'To Bill')
+
+ pi = make_purchase_invoice(pr.name)
+ pi.is_paid = 1
+ pi.mode_of_payment = "Cash"
+ pi.cash_bank_account = "Cash - _TC"
+ pi.paid_amount = pr.grand_total
+ pi.save()
+ pi.submit()
+
+ pi_status = frappe.db.get_value("Purchase Invoice",pi.name,'status')
+ self.assertEqual(pi_status,'Paid')
+
+ po_status_after_paid = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_after_paid,'Completed')
+
+ def test_po_to_pr_to_pi_partly_paid_TC_B_089(self):
+ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+ purchase_tax = frappe.new_doc("Purchase Taxes and Charges Template")
+ purchase_tax.title = "TEST"
+ purchase_tax.company = "_Test Company"
+ purchase_tax.tax_category = "_Test Tax Category 1"
+
+ purchase_tax.append("taxes",{
+ "category":"Total",
+ "add_deduct_tax":"Add",
+ "charge_type":"On Net Total",
+ "account_head":"_Test Account Excise Duty - _TC",
+ "_Test Account Excise Duty":"_Test Account Excise Duty",
+ "rate":100,
+ "description":"GST"
+ })
+
+ purchase_tax.save()
+
+ po = create_purchase_order(do_not_save=True)
+ po.taxes_and_charges = purchase_tax.name
+ po.save()
+ po.submit()
+ po_status_before = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_before,'To Receive and Bill')
+
+ pr = make_purchase_receipt(po.name)
+ pr.save()
+ pr.submit()
+
+ po_status_after_pr = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_after_pr,'To Bill')
+
+ pi = make_purchase_invoice(pr.name)
+ pi.is_paid = 1
+ pi.mode_of_payment = "Cash"
+ pi.cash_bank_account = "Cash - _TC"
+ pi.paid_amount = pr.grand_total / 2
+ pi.save()
+ pi.submit()
+
+ pi_status = frappe.db.get_value("Purchase Invoice",pi.name,'status')
+ self.assertEqual(pi_status,'Partly Paid')
+
+ po_status_after_paid = frappe.db.get_value("Purchase Order",po.name,'status')
+ self.assertEqual(po_status_after_paid,'Completed')
+
+ def test_po_with_additional_discount_TC_B_057(self):
+ company = "_Test Company"
+ item_code = "Testing-31"
+ target_warehouse = "Stores - _TC"
+ supplier = "_Test Supplier 1"
+ item_price = 10000
+ if not frappe.db.exists("Item", item_code):
+ frappe.get_doc({
+ "doctype": "Item",
+ "item_code": item_code,
+ "item_name": item_code,
+ "is_stock_item": 1,
+ "is_purchase_item": 1,
+ "is_sales_item": 0,
+ "company": company
+ }).insert()
+ pi = frappe.get_doc({
+ "doctype": "Purchase Order",
+ "supplier": supplier,
+ "company": company,
+ "schedule_date": today(),
+ "set_warehouse": target_warehouse,
+ "items": [
+ {
+ "item_code": item_code,
+ "warehouse": target_warehouse,
+ "qty": 1,
+ "rate": item_price
+ }
+ ]
+ })
+ pi.insert()
+ self.assertEqual(len(pi.items), 1)
+ self.assertEqual(pi.items[0].rate, item_price)
+ self.assertEqual(pi.net_total, item_price)
+ pi.apply_discount_on = "Net Total"
+ pi.additional_discount_percentage = 10
+ pi.save()
+ pi.submit()
+ self.assertEqual(pi.discount_amount, 1000)
+ self.assertEqual(pi.net_total, 9000)
def create_po_for_sc_testing():
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 932cee51322f..83ffc9be9f29 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2350,6 +2350,7 @@ def validate_payment_schedule_dates(self):
for d in self.get("payment_schedule"):
+ d.validate_from_to_dates("discount_date", "due_date")
if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
frappe.throw(
_("Row {0}: Due Date in the Payment Terms table cannot be before Posting Date").format(
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index f33f2987f23d..89c73b65e1dc 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -1241,6 +1241,7 @@ def add_items_in_ste(ste_doc, row, qty, rm_details, rm_detail_field="sco_rm_deta
"item_code": row.item_details["rm_item_code"],
"subcontracted_item": row.item_details["main_item_code"],
"serial_no": "\n".join(row.serial_no) if row.serial_no else "",
+ "use_serial_batch_fields": 1,
}
)
@@ -1274,11 +1275,14 @@ def make_return_stock_entry_for_subcontract(
for _key, value in available_materials.items():
if not value.qty:
continue
+
+ if item_details := value.get("item_details"):
+ item_details["serial_and_batch_bundle"] = None
if value.batch_no:
for batch_no, qty in value.batch_no.items():
if qty > 0:
- add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field, batch_no)
+ add_items_in_ste(ste_doc, value, qty, rm_details, rm_detail_field, batch_no)
else:
add_items_in_ste(ste_doc, value, value.qty, rm_details, rm_detail_field)
diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py
index 761720d4ef73..41a2f6fc5d28 100644
--- a/erpnext/controllers/tests/test_subcontracting_controller.py
+++ b/erpnext/controllers/tests/test_subcontracting_controller.py
@@ -282,6 +282,74 @@ def test_subcontracting_with_same_components_different_fg(self):
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+
+ def test_return_non_consumed_batch_materials(self):
+ """
+ - Set backflush based on Material Transfer.
+ - Create SCO for item Subcontracted Item SA2.
+ - Transfer the batched components from Stores to Supplier warehouse with serial nos.
+ - Transfer extra qty of component for the subcontracted item Subcontracted Item SA2.
+ - Create SCR for full qty against the SCO and change the qty of raw material.
+ - After that return the non consumed material back to the store from supplier's warehouse.
+ """
+ frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
+ set_backflush_based_on("Material Transferred for Subcontract")
+ service_item = make_item("Subcontracted Service FG Item A", properties={"is_stock_item": 0}).name
+ fg_item = make_item(
+ "Subcontracted FG Item SA2", properties={"is_stock_item": 1, "is_sub_contracted_item": 1}
+ ).name
+ rm_item = make_item(
+ "Subcontracted Batch RM Item SA2",
+ properties={
+ "is_stock_item": 1,
+ "create_new_batch": 1,
+ "has_batch_no": 1,
+ "batch_number_series": "BATCH-RM-IRM-.####",
+ },
+ ).name
+ make_bom(item=fg_item, raw_materials=[rm_item], rate=100, currency="INR")
+ service_items = [
+ {
+ "warehouse": "_Test Warehouse - _TC",
+ "item_code": service_item,
+ "qty": 5,
+ "rate": 100,
+ "fg_item": fg_item,
+ "fg_item_qty": 5,
+ },
+ ]
+ sco = get_subcontracting_order(service_items=service_items)
+ rm_items = get_rm_items(sco.supplied_items)
+ rm_items[0]["qty"] += 1
+ itemwise_details = make_stock_in_entry(rm_items=rm_items)
+ for item in rm_items:
+ item["sco_rm_detail"] = sco.items[0].name
+ make_stock_transfer_entry(
+ sco_no=sco.name,
+ rm_items=rm_items,
+ itemwise_details=copy.deepcopy(itemwise_details),
+ )
+ scr1 = make_subcontracting_receipt(sco.name)
+ scr1.save()
+ scr1.supplied_items[0].consumed_qty = 5
+ scr1.submit()
+ for key, value in get_supplied_items(scr1).items():
+ transferred_detais = itemwise_details.get(key)
+ self.assertEqual(value.qty, 5)
+ self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5]))
+ sco.load_from_db()
+ self.assertEqual(sco.supplied_items[0].consumed_qty, 5)
+ doc = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items])
+ doc.save()
+ self.assertEqual(doc.items[0].qty, 1)
+ self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC")
+ self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC")
+ self.assertTrue(doc.items[0].batch_no)
+ self.assertTrue(doc.items[0].use_serial_batch_fields)
+ frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+
+
+
def test_return_non_consumed_materials(self):
"""
- Set backflush based on Material Transfer.
diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
index eb4135e03ac0..2c0f091da588 100644
--- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html
+++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html
@@ -3,7 +3,7 @@
{% if data.image %}
-
+
{% endif %}
diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
index 2e8dba1ccfeb..af3b9d5149ea 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -9,7 +9,7 @@
import frappe
from frappe.model.document import Document
-
+from frappe.utils import date_diff, get_datetime, now
class BOMUpdateTool(Document):
# begin: auto-generated types
@@ -50,12 +50,18 @@ def auto_update_latest_price_in_all_boms() -> None:
if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"):
wip_log = frappe.get_all(
"BOM Update Log",
- {"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
+ fields=["creation", "status"],
+ filters={"update_type": "Update Cost", "status": ["in", ["Queued", "In Progress"]]},
limit_page_length=1,
+ order_by="creation desc",
)
- if not wip_log:
+ if not wip_log or is_older_log(wip_log[0]):
create_bom_update_log(update_type="Update Cost")
+def is_older_log(log: dict) -> bool:
+ no_of_days = date_diff(get_datetime(now()), get_datetime(log.creation))
+ return no_of_days > 10
+
def create_bom_update_log(
boms: dict[str, str] | None = None,
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index cb766762b342..6332fa4ae73f 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -242,7 +242,7 @@
"depends_on": "eval:doc.backflush_raw_materials_based_on == \"BOM\"",
"fieldname": "validate_components_quantities_per_bom",
"fieldtype": "Check",
- "label": "Validate Components Quantities Per BOM"
+ "label": "Validate Components and Quantities Per BOM"
}
],
"icon": "icon-wrench",
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 5e2c68883752..9c320edd4baa 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -2198,6 +2198,44 @@ def test_components_qty_for_bom_based_manufacture_entry(self):
manufacture_entry.submit()
frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0)
+ def test_components_as_per_bom_for_manufacture_entry(self):
+ frappe.db.set_single_value("Manufacturing Settings", "backflush_raw_materials_based_on", "BOM")
+ frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 1)
+ fg_item = "Test FG Item For Component Validation 1"
+ source_warehouse = "Stores - _TC"
+ raw_materials = ["Test Component Validation RM Item 11", "Test Component Validation RM Item 12"]
+ make_item(fg_item, {"is_stock_item": 1})
+ for item in raw_materials:
+ make_item(item, {"is_stock_item": 1})
+ test_stock_entry.make_stock_entry(
+ item_code=item,
+ target=source_warehouse,
+ qty=10,
+ basic_rate=100,
+ )
+ make_bom(item=fg_item, source_warehouse=source_warehouse, raw_materials=raw_materials)
+ wo = make_wo_order_test_record(
+ item=fg_item,
+ qty=10,
+ source_warehouse=source_warehouse,
+ )
+ transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
+ transfer_entry.save()
+ transfer_entry.remove(transfer_entry.items[0])
+ self.assertRaises(frappe.ValidationError, transfer_entry.save)
+ transfer_entry = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
+ transfer_entry.save()
+ transfer_entry.submit()
+ manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
+ manufacture_entry.save()
+ manufacture_entry.remove(manufacture_entry.items[0])
+ self.assertRaises(frappe.ValidationError, manufacture_entry.save)
+ manufacture_entry.delete()
+ manufacture_entry = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
+ manufacture_entry.save()
+ manufacture_entry.submit()
+ frappe.db.set_single_value("Manufacturing Settings", "validate_components_quantities_per_bom", 0)
+
def make_operation(**kwargs):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index ef17ede9c7cd..43e90708f731 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -122,6 +122,31 @@ frappe.ui.form.on("Work Order", {
);
},
+ allow_alternative_item: function (frm) {
+ let has_alternative = false;
+ if (frm.doc.required_items) {
+ has_alternative = frm.doc.required_items.find((i) => i.allow_alternative_item === 1);
+ }
+ if (frm.doc.allow_alternative_item && frm.doc.docstatus === 0 && has_alternative) {
+ frm.add_custom_button(__("Alternate Item"), () => {
+ erpnext.utils.select_alternate_items({
+ frm: frm,
+ child_docname: "required_items",
+ warehouse_field: "source_warehouse",
+ child_doctype: "Work Order Item",
+ original_item_field: "original_item",
+ condition: (d) => {
+ if (d.allow_alternative_item) {
+ return true;
+ }
+ },
+ });
+ });
+ } else {
+ frm.remove_custom_button(__("Alternate Item"));
+ }
+ },
+
refresh: function (frm) {
erpnext.toggle_naming_series();
erpnext.work_order.set_custom_buttons(frm);
@@ -155,25 +180,6 @@ frappe.ui.form.on("Work Order", {
}
}
- if (frm.doc.required_items && frm.doc.allow_alternative_item) {
- const has_alternative = frm.doc.required_items.find((i) => i.allow_alternative_item === 1);
- if (frm.doc.docstatus == 0 && has_alternative) {
- frm.add_custom_button(__("Alternate Item"), () => {
- erpnext.utils.select_alternate_items({
- frm: frm,
- child_docname: "required_items",
- warehouse_field: "source_warehouse",
- child_doctype: "Work Order Item",
- original_item_field: "original_item",
- condition: (d) => {
- if (d.allow_alternative_item) {
- return true;
- }
- },
- });
- });
- }
- }
if (frm.doc.status == "Completed") {
if (frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") {
@@ -201,7 +207,7 @@ frappe.ui.form.on("Work Order", {
);
}
- frm.trigger("add_custom_button_to_return_components");
+ frm.trigger("allow_alternative_item");
},
add_custom_button_to_return_components: function (frm) {
@@ -526,6 +532,9 @@ frappe.ui.form.on("Work Order", {
});
frappe.ui.form.on("Work Order Item", {
+ allow_alternative_item(frm) {
+ frm.trigger("allow_alternative_item");
+ },
source_warehouse: function (frm, cdt, cdn) {
var row = locals[cdt][cdn];
if (!row.item_code) {
@@ -604,7 +613,7 @@ erpnext.work_order = {
set_custom_buttons: function (frm) {
var doc = frm.doc;
- if (doc.status !== "Closed") {
+ if (doc.docstatus === 1 && doc.status !== "Closed") {
frm.add_custom_button(
__("Close"),
function () {
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
index 64363e20e394..a5690c477e4c 100644
--- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
@@ -50,7 +50,11 @@ def get_returned_materials(work_orders):
raw_materials = frappe.get_all(
"Stock Entry",
- fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
+ fields=[
+ "`tabStock Entry`.`work_order`",
+ "`tabStock Entry Detail`.`item_code`",
+ "`tabStock Entry Detail`.`qty`",
+ ],
filters=[
["Stock Entry", "is_return", "=", 1],
["Stock Entry Detail", "docstatus", "=", 1],
@@ -59,12 +63,14 @@ def get_returned_materials(work_orders):
)
for d in raw_materials:
- raw_materials_qty[d.item_code] += d.qty
+ key = (d.work_order, d.item_code)
+ raw_materials_qty[key] += d.qty
for row in work_orders:
row.returned_qty = 0.0
- if raw_materials_qty.get(row.raw_material_item_code):
- row.returned_qty = raw_materials_qty.get(row.raw_material_item_code)
+ key = (row.parent, row.raw_material_item_code)
+ if raw_materials_qty.get(key):
+ row.returned_qty = raw_materials_qty.get(key)
def get_fields():
diff --git a/erpnext/patches/v15_0/update_asset_status_to_work_in_progress.py b/erpnext/patches/v15_0/update_asset_status_to_work_in_progress.py
new file mode 100644
index 000000000000..447492989a83
--- /dev/null
+++ b/erpnext/patches/v15_0/update_asset_status_to_work_in_progress.py
@@ -0,0 +1,9 @@
+import frappe
+def execute():
+ Asset = frappe.qb.DocType("Asset")
+ query = (
+ frappe.qb.update(Asset)
+ .set(Asset.status, "Work In Progress")
+ .where((Asset.docstatus == 0) & (Asset.is_composite_asset == 1))
+ )
+ query.run()
\ No newline at end of file
diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
index 4aa087e99cfe..ad5fd528cda9 100644
--- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
@@ -16,7 +16,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
}
make_dt() {
- var me = this;
+ const me = this;
frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
args: {
@@ -193,6 +193,7 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
args: {
bank_account: this.bank_account,
till_date: this.bank_statement_to_date,
+ company: this.company,
},
callback: (response) => (this.cleared_balance = response.message),
});
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index aa7bf029d974..acd2d3a42c16 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -303,11 +303,10 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
}
const me = this;
- if (!this.frm.is_new() && this.frm.doc.docstatus === 0 && frappe.model.can_create("Quality Inspection")) {
+ if (!this.frm.is_new() && this.frm.doc.docstatus === 0 && frappe.model.can_create("Quality Inspection") && this.frm.doc.update_stock) {
this.frm.add_custom_button(__("Quality Inspection(s)"), () => {
me.make_quality_inspection();
}, __("Create"));
- this.frm.page.set_inner_btn_group_as_primary(__('Create'));
}
const inspection_type = ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"].includes(this.frm.doc.doctype)
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 9e35ba7500c0..97fd960deb91 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -1039,6 +1039,18 @@ def test_quotation_to_sales_invoice_to_with_2_delivery_note_TC_S_083(self):
dn_2.submit()
self.stock_check(voucher=dn_2.name,qty=-2)
self.assertEqual(dn_2.status, "Completed")
+
+ def test_quotation_to_material_request_TC_S_084(self):
+ from erpnext.selling.doctype.sales_order.sales_order import make_material_request
+ quotation = self.create_and_submit_quotation("_Test Item Home Desktop 100", 4, 5000, "Stores - _TC")
+ sales_order = self.create_and_submit_sales_order(quotation.name, add_days(nowdate(), 5))
+ quotation.reload()
+ self.assertEqual(quotation.status, "Ordered")
+ mr = make_material_request(sales_order.name)
+ mr.save()
+ mr.submit()
+ mr.reload()
+ self.assertEqual(mr.status, "Pending")
def stock_check(self,voucher,qty):
stock_entries = frappe.get_all(
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index 9a12cce39173..73caacb85f82 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -2598,640 +2598,496 @@ def test_sales_order_for_service_item_TC_S_010(self):
self.assertEqual(si_acc_debit, 5000)
def test_sales_order_full_payment_with_gst_TC_S_011(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=1, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=1, rate=5000)
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ dn = make_delivery_note(so.name)
+ dn.save()
+ dn.submit()
- dn = make_delivery_note(so.name)
- dn.save()
- dn.submit()
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
- self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change[0].get("actual_qty"), -1)
- qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
- self.assertEqual(qty_change[0].get("actual_qty"), -1)
-
- dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
- self.assertEqual(dn_acc_credit1, qty_change[0].get("valuation_rate") * 1)
+ dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
+ self.assertEqual(dn_acc_credit1, qty_change[0].get("valuation_rate") * 1)
- dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
- self.assertEqual(dn_acc_debit1, qty_change[0].get("valuation_rate") * 1)
-
- from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
+ dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
+ self.assertEqual(dn_acc_debit1, qty_change[0].get("valuation_rate") * 1)
- si = make_sales_invoice(dn.name)
- si.insert()
- si.submit()
+ from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
- self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
-
- voucher_params_si = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
+ si = make_sales_invoice(dn.name)
+ si.insert()
+ si.submit()
- self.assertEqual(gl_entries['Sales - _TIRC'], 5000)
- self.assertEqual(gl_entries['Debtors - _TIRC'], 5900)
- self.assertEqual(gl_entries['Output Tax SGST - _TIRC'], 450)
- self.assertEqual(gl_entries['Output Tax CGST - _TIRC'], 450)
-
- dn.reload()
- self.assertEqual(dn.status, "Completed")
+ self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
+
+ voucher_params_si = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+
+ self.assertEqual(gl_entries['Sales - _TIRC'], 5000)
+ self.assertEqual(gl_entries['Debtors - _TIRC'], 5900)
+ self.assertEqual(gl_entries['Output Tax SGST - _TIRC'], 450)
+ self.assertEqual(gl_entries['Output Tax CGST - _TIRC'], 450)
+
+ dn.reload()
+ self.assertEqual(dn.status, "Completed")
def test_sales_order_partial_payment_with_gst_TC_S_012(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ dn1 = make_delivery_note(so.name)
+ dn1.get("items")[0].qty = 2
+ dn1.save()
+ dn1.submit()
- dn1 = make_delivery_note(so.name)
- dn1.get("items")[0].qty = 2
- dn1.save()
- dn1.submit()
+ self.assertEqual(dn1.status, "To Bill", "Delivery Note not created")
- self.assertEqual(dn1.status, "To Bill", "Delivery Note not created")
+ qty_change1 = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn1.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change1[0].get("actual_qty"), -2)
- qty_change1 = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn1.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
- self.assertEqual(qty_change1[0].get("actual_qty"), -2)
-
- dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn1.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
- self.assertEqual(dn_acc_credit1, qty_change1[0].get("valuation_rate") * 2)
+ dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn1.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
+ self.assertEqual(dn_acc_credit1, qty_change1[0].get("valuation_rate") * 2)
- dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn1.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
- self.assertEqual(dn_acc_debit1, qty_change1[0].get("valuation_rate") * 2)
-
- from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
+ dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn1.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
+ self.assertEqual(dn_acc_debit1, qty_change1[0].get("valuation_rate") * 2)
- si1 = make_sales_invoice(dn1.name)
- si1.insert()
- si1.submit()
+ from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
- self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
+ si1 = make_sales_invoice(dn1.name)
+ si1.insert()
+ si1.submit()
- voucher_params_si1 = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si1.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si1 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
- self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
- self.assertEqual(gl_entries_si1['Output Tax SGST - _TIRC'], 900)
- self.assertEqual(gl_entries_si1['Output Tax CGST - _TIRC'], 900)
-
- dn1.reload()
- self.assertEqual(dn1.status, "Completed")
-
- dn2 = make_delivery_note(so.name)
- dn2.save()
- dn2.submit()
+ self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
- qty_change2 = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn2.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
- self.assertEqual(qty_change2[0].get("actual_qty"), -2)
-
- dn_acc_credit2 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn2.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
- self.assertEqual(dn_acc_credit2, qty_change2[0].get("valuation_rate") * 2)
+ voucher_params_si1 = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si1.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si1 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+ self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
+ self.assertEqual(gl_entries_si1['Output Tax SGST - _TIRC'], 900)
+ self.assertEqual(gl_entries_si1['Output Tax CGST - _TIRC'], 900)
+
+ dn1.reload()
+ self.assertEqual(dn1.status, "Completed")
+
+ dn2 = make_delivery_note(so.name)
+ dn2.save()
+ dn2.submit()
- dn_acc_debit2 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn2.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
- self.assertEqual(dn_acc_debit2, qty_change2[0].get("valuation_rate") * 2)
+ qty_change2 = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn2.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change2[0].get("actual_qty"), -2)
- si2 = make_sales_invoice(dn2.name)
- si2.insert()
- si2.submit()
+ dn_acc_credit2 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn2.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
+ self.assertEqual(dn_acc_credit2, qty_change2[0].get("valuation_rate") * 2)
- self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
+ dn_acc_debit2 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn2.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
+ self.assertEqual(dn_acc_debit2, qty_change2[0].get("valuation_rate") * 2)
- voucher_params_si2 = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si2.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si2 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
- self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
- self.assertEqual(gl_entries_si2['Output Tax SGST - _TIRC'], 900)
- self.assertEqual(gl_entries_si2['Output Tax CGST - _TIRC'], 900)
-
- dn2.reload()
- self.assertEqual(dn2.status, "Completed")
+ si2 = make_sales_invoice(dn2.name)
+ si2.insert()
+ si2.submit()
+
+ self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
+
+ voucher_params_si2 = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si2.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si2 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+ self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
+ self.assertEqual(gl_entries_si2['Output Tax SGST - _TIRC'], 900)
+ self.assertEqual(gl_entries_si2['Output Tax CGST - _TIRC'], 900)
+
+ dn2.reload()
+ self.assertEqual(dn2.status, "Completed")
def test_sales_order_partial_sales_invoice_with_gst_TC_S_013(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
+
+ dn = make_delivery_note(so.name)
+ dn.save()
+ dn.submit()
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
- dn = make_delivery_note(so.name)
- dn.save()
- dn.submit()
+ qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change[0].get("actual_qty"), -4)
- self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
+ self.assertEqual(dn_acc_credit1, qty_change[0].get("valuation_rate") * 4)
- qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
- self.assertEqual(qty_change[0].get("actual_qty"), -4)
-
- dn_acc_credit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
- self.assertEqual(dn_acc_credit1, qty_change[0].get("valuation_rate") * 4)
+ dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
+ self.assertEqual(dn_acc_debit1, qty_change[0].get("valuation_rate") * 4)
- dn_acc_debit1 = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
- self.assertEqual(dn_acc_debit1, qty_change[0].get("valuation_rate") * 4)
-
- from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
+ from erpnext.stock.doctype.delivery_note.delivery_note import (make_sales_invoice)
- si1 = make_sales_invoice(dn.name)
- si1.get("items")[0].qty = 2
- si1.insert()
- si1.submit()
+ si1 = make_sales_invoice(dn.name)
+ si1.get("items")[0].qty = 2
+ si1.insert()
+ si1.submit()
- self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
+ self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
- voucher_params_si1 = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si1.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si1 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
- self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
- self.assertEqual(gl_entries_si1['Output Tax SGST - _TIRC'], 900)
- self.assertEqual(gl_entries_si1['Output Tax CGST - _TIRC'], 900)
-
- si2 = make_sales_invoice(dn.name)
- si2.insert()
- si2.submit()
+ voucher_params_si1 = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si1.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si1 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+ self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
+ self.assertEqual(gl_entries_si1['Output Tax SGST - _TIRC'], 900)
+ self.assertEqual(gl_entries_si1['Output Tax CGST - _TIRC'], 900)
- self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
+ si2 = make_sales_invoice(dn.name)
+ si2.insert()
+ si2.submit()
- voucher_params_si2 = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si2.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si2 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
- self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
- self.assertEqual(gl_entries_si2['Output Tax SGST - _TIRC'], 900)
- self.assertEqual(gl_entries_si2['Output Tax CGST - _TIRC'], 900)
-
- dn.reload()
- self.assertEqual(dn.status, "Completed")
+ self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
+
+ voucher_params_si2 = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si2.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si2 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+ self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
+ self.assertEqual(gl_entries_si2['Output Tax SGST - _TIRC'], 900)
+ self.assertEqual(gl_entries_si2['Output Tax CGST - _TIRC'], 900)
+
+ dn.reload()
+ self.assertEqual(dn.status, "Completed")
def test_sales_order_create_dn_via_si_with_gst_TC_S_014(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
-
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
- si = make_sales_invoice(so.name)
- si.save()
- si.submit()
+ si = make_sales_invoice(so.name)
+ si.save()
+ si.submit()
- self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
-
- si_acc_credit = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit')
- self.assertEqual(si_acc_credit, 20000)
+ self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
- si_acc_debit = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit')
- self.assertEqual(si_acc_debit, 23600)
-
- si_acc_credit_gst = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit')
- self.assertEqual(si_acc_credit_gst, 1800)
+ si_acc_credit = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit')
+ self.assertEqual(si_acc_credit, 20000)
- si_acc_debit_gst = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit')
- self.assertEqual(si_acc_debit_gst, 1800)
-
- from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
- dn = make_delivery_note(si.name)
- dn.save()
- dn.submit()
+ si_acc_debit = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit')
+ self.assertEqual(si_acc_debit, 23600)
+
+ si_acc_credit_gst = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit')
+ self.assertEqual(si_acc_credit_gst, 1800)
- self.assertEqual(dn.status, "Completed", "Delivery Note not created")
+ si_acc_debit_gst = frappe.db.get_value('GL Entry', {'voucher_type': 'Sales Invoice', 'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit')
+ self.assertEqual(si_acc_debit_gst, 1800)
- qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
- self.assertEqual(qty_change[0].get("actual_qty"), -4)
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
+ dn = make_delivery_note(si.name)
+ dn.save()
+ dn.submit()
- dn_acc_credit = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
- self.assertEqual(dn_acc_credit, qty_change[0].get("valuation_rate") * 4)
+ self.assertEqual(dn.status, "Completed", "Delivery Note not created")
- dn_acc_debit = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
- self.assertEqual(dn_acc_debit, qty_change[0].get("valuation_rate") * 4)
+ qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change[0].get("actual_qty"), -4)
+
+ dn_acc_credit = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit')
+ self.assertEqual(dn_acc_credit, qty_change[0].get("valuation_rate") * 4)
+
+ dn_acc_debit = frappe.db.get_value('GL Entry', {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit')
+ self.assertEqual(dn_acc_debit, qty_change[0].get("valuation_rate") * 4)
def test_sales_order_create_partial_dn_via_si_with_gst_TC_S_015(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
+
+ si = make_sales_invoice(so.name)
+ si.save()
+ si.submit()
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
-
- si = make_sales_invoice(so.name)
- si.save()
- si.submit()
+ self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
- self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
-
- voucher_params_si = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name
- }
- gl_accounts = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
- for account, field in gl_accounts.items()
- }
- self.assertEqual(gl_entries['Sales - _TIRC'], 20000)
- self.assertEqual(gl_entries['Debtors - _TIRC'], 23600)
- self.assertEqual(gl_entries['Output Tax SGST - _TIRC'], 1800)
- self.assertEqual(gl_entries['Output Tax CGST - _TIRC'], 1800)
-
- from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
- dn1 = make_delivery_note(si.name)
- dn1.get("items")[0].qty = 2
- dn1.save()
- dn1.submit()
+ voucher_params_si = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si.name
+ }
+ gl_accounts = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
+ for account, field in gl_accounts.items()
+ }
+ self.assertEqual(gl_entries['Sales - _TIRC'], 20000)
+ self.assertEqual(gl_entries['Debtors - _TIRC'], 23600)
+ self.assertEqual(gl_entries['Output Tax SGST - _TIRC'], 1800)
+ self.assertEqual(gl_entries['Output Tax CGST - _TIRC'], 1800)
- self.assertEqual(dn1.status, "Completed", "Delivery Note not created")
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_delivery_note
+ dn1 = make_delivery_note(si.name)
+ dn1.get("items")[0].qty = 2
+ dn1.save()
+ dn1.submit()
- qty_change1 = frappe.get_all(
- 'Stock Ledger Entry',
- {'item_code': '_Test Item', 'voucher_no': dn1.name, 'warehouse': 'Stores - _TIRC'},
- ['actual_qty', 'valuation_rate']
- )
- actual_qty = qty_change1[0].get("actual_qty")
- valuation_rate = qty_change1[0].get("valuation_rate")
+ self.assertEqual(dn1.status, "Completed", "Delivery Note not created")
- self.assertEqual(actual_qty, -2)
+ qty_change1 = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'item_code': '_Test Item', 'voucher_no': dn1.name, 'warehouse': 'Stores - _TIRC'},
+ ['actual_qty', 'valuation_rate']
+ )
+ actual_qty = qty_change1[0].get("actual_qty")
+ valuation_rate = qty_change1[0].get("valuation_rate")
- voucher_params_dn1 = {
- 'voucher_type': 'Delivery Note','voucher_no': dn1.name
- }
- gl_accounts_dn1 = {
- 'Stock In Hand - _TIRC': 'credit','Cost of Goods Sold - _TIRC': 'debit'
- }
- gl_entries_dn1 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_dn1, 'account': account}, field)
- for account, field in gl_accounts_dn1.items()
- }
- self.assertEqual(gl_entries_dn1['Stock In Hand - _TIRC'], valuation_rate * 2)
- self.assertEqual(gl_entries_dn1['Cost of Goods Sold - _TIRC'], valuation_rate * 2)
-
- dn2 = make_delivery_note(si.name)
- dn2.save()
- dn2.submit()
-
- self.assertEqual(dn1.status, "Completed", "Delivery Note not created")
+ self.assertEqual(actual_qty, -2)
- qty_change2 = frappe.get_all(
- 'Stock Ledger Entry',
- {'item_code': '_Test Item', 'voucher_no': dn2.name, 'warehouse': 'Stores - _TIRC'},
- ['actual_qty', 'valuation_rate']
- )
- actual_qty = qty_change2[0].get("actual_qty")
- valuation_rate = qty_change2[0].get("valuation_rate")
+ voucher_params_dn1 = {
+ 'voucher_type': 'Delivery Note','voucher_no': dn1.name
+ }
+ gl_accounts_dn1 = {
+ 'Stock In Hand - _TIRC': 'credit','Cost of Goods Sold - _TIRC': 'debit'
+ }
+ gl_entries_dn1 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_dn1, 'account': account}, field)
+ for account, field in gl_accounts_dn1.items()
+ }
+ self.assertEqual(gl_entries_dn1['Stock In Hand - _TIRC'], valuation_rate * 2)
+ self.assertEqual(gl_entries_dn1['Cost of Goods Sold - _TIRC'], valuation_rate * 2)
+
+ dn2 = make_delivery_note(si.name)
+ dn2.save()
+ dn2.submit()
- self.assertEqual(actual_qty, -2)
+ self.assertEqual(dn1.status, "Completed", "Delivery Note not created")
- voucher_params_dn2 = {
- 'voucher_type': 'Delivery Note',
- 'voucher_no': dn2.name
- }
- gl_accounts_dn2 = {
- 'Stock In Hand - _TIRC': 'credit',
- 'Cost of Goods Sold - _TIRC': 'debit'
- }
- gl_entries_dn2 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_dn2, 'account': account}, field)
- for account, field in gl_accounts_dn2.items()
- }
- self.assertEqual(gl_entries_dn2['Stock In Hand - _TIRC'], valuation_rate * 2)
- self.assertEqual(gl_entries_dn2['Cost of Goods Sold - _TIRC'], valuation_rate * 2)
+ qty_change2 = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'item_code': '_Test Item', 'voucher_no': dn2.name, 'warehouse': 'Stores - _TIRC'},
+ ['actual_qty', 'valuation_rate']
+ )
+ actual_qty = qty_change2[0].get("actual_qty")
+ valuation_rate = qty_change2[0].get("valuation_rate")
+
+ self.assertEqual(actual_qty, -2)
+
+ voucher_params_dn2 = {
+ 'voucher_type': 'Delivery Note',
+ 'voucher_no': dn2.name
+ }
+ gl_accounts_dn2 = {
+ 'Stock In Hand - _TIRC': 'credit',
+ 'Cost of Goods Sold - _TIRC': 'debit'
+ }
+ gl_entries_dn2 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_dn2, 'account': account}, field)
+ for account, field in gl_accounts_dn2.items()
+ }
+ self.assertEqual(gl_entries_dn2['Stock In Hand - _TIRC'], valuation_rate * 2)
+ self.assertEqual(gl_entries_dn2['Cost of Goods Sold - _TIRC'], valuation_rate * 2)
def test_sales_order_update_stock_in_si_with_gst_TC_S_018(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ si = make_sales_invoice(so.name)
+ si.update_stock = 1
+ si.save()
+ si.submit()
- si = make_sales_invoice(so.name)
- si.update_stock = 1
- si.save()
- si.submit()
+ self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
- self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
+ qty_change = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'item_code': '_Test Item', 'voucher_no': si.name, 'warehouse': 'Stores - _TIRC'},
+ ['actual_qty', 'valuation_rate']
+ )
+ actual_qty = qty_change[0].get("actual_qty")
+ valuation_rate = qty_change[0].get("valuation_rate")
- qty_change = frappe.get_all(
- 'Stock Ledger Entry',
- {'item_code': '_Test Item', 'voucher_no': si.name, 'warehouse': 'Stores - _TIRC'},
- ['actual_qty', 'valuation_rate']
- )
- actual_qty = qty_change[0].get("actual_qty")
- valuation_rate = qty_change[0].get("valuation_rate")
+ self.assertEqual(actual_qty, -4)
- self.assertEqual(actual_qty, -4)
+ voucher_params_si = {
+ 'voucher_type': 'Sales Invoice',
+ 'voucher_no': si.name
+ }
+ gl_accounts_si = {
+ 'Stock In Hand - _TIRC': 'credit',
+ 'Cost of Goods Sold - _TIRC': 'debit',
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
+ for account, field in gl_accounts_si.items()
+ }
+ self.assertEqual(gl_entries_si['Stock In Hand - _TIRC'], valuation_rate * 4)
+ self.assertEqual(gl_entries_si['Cost of Goods Sold - _TIRC'], valuation_rate * 4)
+ self.assertEqual(gl_entries_si['Sales - _TIRC'], 20000)
+ self.assertEqual(gl_entries_si['Debtors - _TIRC'], 23600)
+ self.assertEqual(gl_entries_si['Output Tax SGST - _TIRC'], 1800)
+ self.assertEqual(gl_entries_si['Output Tax CGST - _TIRC'], 1800)
- voucher_params_si = {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name
- }
- gl_accounts_si = {
- 'Stock In Hand - _TIRC': 'credit',
- 'Cost of Goods Sold - _TIRC': 'debit',
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
- for account, field in gl_accounts_si.items()
- }
- self.assertEqual(gl_entries_si['Stock In Hand - _TIRC'], valuation_rate * 4)
- self.assertEqual(gl_entries_si['Cost of Goods Sold - _TIRC'], valuation_rate * 4)
- self.assertEqual(gl_entries_si['Sales - _TIRC'], 20000)
- self.assertEqual(gl_entries_si['Debtors - _TIRC'], 23600)
- self.assertEqual(gl_entries_si['Output Tax SGST - _TIRC'], 1800)
- self.assertEqual(gl_entries_si['Output Tax CGST - _TIRC'], 1800)
-
- so.reload()
- self.assertEqual(so.status, 'Completed')
+ so.reload()
+ self.assertEqual(so.status, 'Completed')
def test_sales_order_update_stock_in_partial_si_with_gst_TC_S_019(self):
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='_Test Item', qty=4, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=4, rate=5000)
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ si1 = make_sales_invoice(so.name)
+ si1.get("items")[0].qty = 2
+ si1.update_stock = 1
+ si1.save()
+ si1.submit()
- si1 = make_sales_invoice(so.name)
- si1.get("items")[0].qty = 2
- si1.update_stock = 1
- si1.save()
- si1.submit()
+ self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
- self.assertEqual(si1.status, "Unpaid", "Sales Invoice not created")
+ qty_change1 = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'item_code': '_Test Item', 'voucher_no': si1.name, 'warehouse': 'Stores - _TIRC'},
+ ['actual_qty', 'valuation_rate']
+ )
- qty_change1 = frappe.get_all(
- 'Stock Ledger Entry',
- {'item_code': '_Test Item', 'voucher_no': si1.name, 'warehouse': 'Stores - _TIRC'},
- ['actual_qty', 'valuation_rate']
- )
+ actual_qty1 = qty_change1[0].get("actual_qty")
+ valuation_rate1 = qty_change1[0].get("valuation_rate")
- actual_qty1 = qty_change1[0].get("actual_qty")
- valuation_rate1 = qty_change1[0].get("valuation_rate")
+ self.assertEqual(actual_qty1, -2)
- self.assertEqual(actual_qty1, -2)
+ voucher_params_si1 = {
+ 'voucher_type': 'Sales Invoice','voucher_no': si1.name
+ }
+ gl_accounts_si1 = {
+ 'Stock In Hand - _TIRC': 'credit',
+ 'Cost of Goods Sold - _TIRC': 'debit',
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit'
+ }
+ gl_entries_si1 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
+ for account, field in gl_accounts_si1.items()
+ }
+ self.assertEqual(gl_entries_si1['Stock In Hand - _TIRC'], valuation_rate1 * 2)
+ self.assertEqual(gl_entries_si1['Cost of Goods Sold - _TIRC'], valuation_rate1 * 2)
+ self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
- voucher_params_si1 = {
- 'voucher_type': 'Sales Invoice','voucher_no': si1.name
- }
- gl_accounts_si1 = {
- 'Stock In Hand - _TIRC': 'credit',
- 'Cost of Goods Sold - _TIRC': 'debit',
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit'
- }
- gl_entries_si1 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si1, 'account': account}, field)
- for account, field in gl_accounts_si1.items()
- }
- self.assertEqual(gl_entries_si1['Stock In Hand - _TIRC'], valuation_rate1 * 2)
- self.assertEqual(gl_entries_si1['Cost of Goods Sold - _TIRC'], valuation_rate1 * 2)
- self.assertEqual(gl_entries_si1['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si1['Debtors - _TIRC'], 11800)
-
- si2 = make_sales_invoice(so.name)
- si2.update_stock = 1
- si2.save()
- si2.submit()
+ si2 = make_sales_invoice(so.name)
+ si2.update_stock = 1
+ si2.save()
+ si2.submit()
- self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
+ self.assertEqual(si2.status, "Unpaid", "Sales Invoice not created")
- qty_change2 = frappe.get_all(
- 'Stock Ledger Entry',
- {'item_code': '_Test Item', 'voucher_no': si2.name, 'warehouse': 'Stores - _TIRC'},
- ['actual_qty', 'valuation_rate']
- )
- actual_qty2 = qty_change2[0].get("actual_qty")
- valuation_rate2 = qty_change2[0].get("valuation_rate")
+ qty_change2 = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'item_code': '_Test Item', 'voucher_no': si2.name, 'warehouse': 'Stores - _TIRC'},
+ ['actual_qty', 'valuation_rate']
+ )
+ actual_qty2 = qty_change2[0].get("actual_qty")
+ valuation_rate2 = qty_change2[0].get("valuation_rate")
- self.assertEqual(actual_qty2, -2)
+ self.assertEqual(actual_qty2, -2)
- voucher_params_si2 = {
- 'voucher_type': 'Sales Invoice','voucher_no': si2.name
- }
- gl_accounts_si2 = {
- 'Stock In Hand - _TIRC': 'credit',
- 'Cost of Goods Sold - _TIRC': 'debit',
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit'
- }
- gl_entries_si2 = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
- for account, field in gl_accounts_si2.items()
- }
- self.assertEqual(gl_entries_si2['Stock In Hand - _TIRC'], valuation_rate2 * 2)
- self.assertEqual(gl_entries_si2['Cost of Goods Sold - _TIRC'], valuation_rate2 * 2)
- self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
- self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
+ voucher_params_si2 = {
+ 'voucher_type': 'Sales Invoice','voucher_no': si2.name
+ }
+ gl_accounts_si2 = {
+ 'Stock In Hand - _TIRC': 'credit',
+ 'Cost of Goods Sold - _TIRC': 'debit',
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit'
+ }
+ gl_entries_si2 = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si2, 'account': account}, field)
+ for account, field in gl_accounts_si2.items()
+ }
+ self.assertEqual(gl_entries_si2['Stock In Hand - _TIRC'], valuation_rate2 * 2)
+ self.assertEqual(gl_entries_si2['Cost of Goods Sold - _TIRC'], valuation_rate2 * 2)
+ self.assertEqual(gl_entries_si2['Sales - _TIRC'], 10000)
+ self.assertEqual(gl_entries_si2['Debtors - _TIRC'], 11800)
- so.reload()
- self.assertEqual(so.status, 'Completed')
+ so.reload()
+ self.assertEqual(so.status, 'Completed')
def test_sales_order_for_service_item_with_gst_TC_S_020(self):
make_service_item()
- company = frappe.get_all("Company", {"name": "_Test Indian Registered Company"}, ["gstin", "gst_category"])
- customer = frappe.get_all("Customer", {"name": "_Test Registered Customer"}, ["gstin", "gst_category"])
- company_add = frappe.get_all("Address", {"name": "_Test Indian Registered Company-Billing"}, ["name", "gstin", "gst_category"])
- customer_add = frappe.get_all("Address", {"name": "_Test Registered Customer-Billing"}, ["name", "gstin", "gst_category"])
-
- if company[0].get("gst_category") == "Registered Regular" and customer[0].get("gst_category") == "Registered Regular" and customer[0].get("gstin") and customer[0].get("gstin"):
- if company_add[0].get("gst_category") == "Registered Regular" and customer_add[0].get("gst_category") == "Registered Regular" and company_add[0].get("gstin") and customer_add[0].get("gstin"):
- so = make_sales_order(company='_Test Indian Registered Company', customer='_Test Registered Customer', warehouse='Stores - _TIRC' ,cost_center='Main - _TIRC', selling_price_list='Standard Selling', item_code='Consultancy', qty=1, rate=5000, do_not_save=True)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
- so.save()
- so.submit()
-
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
-
- si = make_sales_invoice(so.name)
- si.save()
- si.submit()
-
- self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=1, rate=5000)
- voucher_params_si = {
- 'voucher_type': 'Sales Invoice','voucher_no': si.name
- }
- gl_accounts_si = {
- 'Sales - _TIRC': 'credit',
- 'Debtors - _TIRC': 'debit',
- 'Output Tax SGST - _TIRC': 'credit',
- 'Output Tax CGST - _TIRC': 'credit'
- }
- gl_entries_si = {
- account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
- for account, field in gl_accounts_si.items()
- }
- self.assertEqual(gl_entries_si['Sales - _TIRC'], 5000)
- self.assertEqual(gl_entries_si['Debtors - _TIRC'], 5900)
- self.assertEqual(gl_entries_si['Output Tax SGST - _TIRC'], 450)
- self.assertEqual(gl_entries_si['Output Tax CGST - _TIRC'], 450)
+ si = make_sales_invoice(so.name)
+ si.save()
+ si.submit()
+
+ self.assertEqual(si.status, "Unpaid", "Sales Invoice not created")
+
+ voucher_params_si = {
+ 'voucher_type': 'Sales Invoice','voucher_no': si.name
+ }
+ gl_accounts_si = {
+ 'Sales - _TIRC': 'credit',
+ 'Debtors - _TIRC': 'debit',
+ 'Output Tax SGST - _TIRC': 'credit',
+ 'Output Tax CGST - _TIRC': 'credit'
+ }
+ gl_entries_si = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_si, 'account': account}, field)
+ for account, field in gl_accounts_si.items()
+ }
+ self.assertEqual(gl_entries_si['Sales - _TIRC'], 5000)
+ self.assertEqual(gl_entries_si['Debtors - _TIRC'], 5900)
+ self.assertEqual(gl_entries_si['Output Tax SGST - _TIRC'], 450)
+ self.assertEqual(gl_entries_si['Output Tax CGST - _TIRC'], 450)
def test_sales_order_of_full_payment_with_shipping_rule_TC_S_021(self):
so = make_sales_order(cost_center='Main - _TC', selling_price_list='Standard Selling', qty=1, rate=5000, do_not_save=True)
@@ -4013,37 +3869,7 @@ def validate_gl_entries(voucher_type, voucher_no, accounts):
self.assertEqual(so.status, "To Deliver", "Sales Order not updated")
def test_sales_order_create_si_via_pe_dn_with_gst_TC_S_042(self):
- company = get_gst_details("Company", {"name": "_Test Indian Registered Company"})[0]
- customer = get_gst_details("Customer", {"name": "_Test Registered Customer"})[0]
- company_add = get_gst_details("Address", {"name": "_Test Indian Registered Company-Billing"})[0]
- customer_add = get_gst_details("Address", {"name": "_Test Registered Customer-Billing"})[0]
-
- if not (is_registered_regular(company) and is_registered_regular(customer) and
- is_registered_regular(company_add) and is_registered_regular(customer_add)):
- self.fail("GST details are not properly configured")
-
- so = make_sales_order(
- company="_Test Indian Registered Company",
- customer="_Test Registered Customer",
- warehouse="Stores - _TIRC",
- cost_center="Main - _TIRC",
- selling_price_list="Standard Selling",
- item_code="_Test Item",
- qty=1,
- rate=5000,
- do_not_save=True
- )
- so.tax_category = "In-State"
- so.taxes_and_charges = "Output GST In-state - _TIRC"
- so.customer_address = customer_add.get("name")
- so.billing_address_gstin = customer_add.get("gstin")
- so.company_address = company_add.get("name")
- so.company_gstin = company_add.get("gstin")
- so.save()
- so.submit()
-
- self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=1, rate=5000)
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
@@ -4120,110 +3946,385 @@ def test_sales_order_create_si_via_pe_dn_with_gst_TC_S_042(self):
)
def test_sales_order_create_si_via_partial_pe_dn_with_gst_TC_S_043(self):
- company = get_gst_details("Company", {"name": "_Test Indian Registered Company"})
- customer = get_gst_details("Customer", {"name": "_Test Registered Customer"})
- company_add = get_gst_details("Address", {"name": "_Test Indian Registered Company-Billing"})
- customer_add = get_gst_details("Address", {"name": "_Test Registered Customer-Billing"})
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=1, rate=5000)
+
+ create_registered_bank_account()
- if not (is_registered_regular(company[0]) and is_registered_regular(customer[0]) and
- is_registered_regular(company_add[0]) and is_registered_regular(customer_add[0])):
- self.fail("GST details are not properly configured")
+ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+ pe = get_payment_entry(dt="Sales Order",dn=so.name)
+ pe.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.paid_amount= 2000
+ for i in pe.references:
+ i.allocated_amount = 2000
+ pe.save()
+ pe.submit()
+
+ self.assertEqual(pe.status, 'Submitted')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Debtors - _TIRC'}, 'credit'), 2000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': '_Test Registered Bank Account - _TIRC'}, 'debit'), 2000)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
+
+ stock_entry = frappe.get_all('Stock Ledger Entry', {
+ 'voucher_no': dn.name,
+ 'warehouse': 'Stores - _TIRC',
+ 'item_code': '_Test Item'
+ }, ['valuation_rate', 'actual_qty'])[0]
+ self.assertEqual(stock_entry.get("actual_qty"), -1)
+
+ dn_acc_value = stock_entry.get("valuation_rate")
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit'), dn_acc_value)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit'), dn_acc_value)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ si = make_sales_invoice(dn.name)
+ si.allocate_advances_automatically = 1
+ si.save()
+ si.submit()
+
+ si.reload()
+ self.assertEqual(si.status, 'Partly Paid')
+
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit'), 5900)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit'), 450)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit'), 450)
+ pe2 = get_payment_entry(dt="Sales Invoice",dn=si.name)
+ pe2.reference_no = "1"
+ pe2.reference_date = nowdate()
+ pe2.save()
+ pe2.submit()
+
+ self.assertEqual(pe2.status, 'Submitted')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Debtors - _TIRC'}, 'credit'), 3900)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': '_Test Registered Bank Account - _TIRC'}, 'debit'), 3900)
+
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 0)
+ self.assertEqual(si.status, "Paid")
+
+ def test_sales_order_with_full_advance_payment_and_shipping_rule_TC_S_044(self):
so = make_sales_order(
- company='_Test Indian Registered Company',
- customer='_Test Registered Customer',
- warehouse='Stores - _TIRC',
- cost_center='Main - _TIRC',
- selling_price_list='Standard Selling',
- item_code='_Test Item',
- qty=1,
- rate=5000,
+ cost_center='Main - _TC',
+ selling_price_list='Standard Selling',
+ qty=1,
+ rate=5000,
do_not_save=True
)
- so.tax_category = 'In-State'
- so.taxes_and_charges = 'Output GST In-state - _TIRC'
- so.customer_address = customer_add[0].get("name")
- so.billing_address_gstin = customer_add[0].get("gstin")
- so.company_address = company_add[0].get("name")
- so.company_gstin = company_add[0].get("gstin")
+ so.shipping_rule = "_Test Shipping Rule"
so.save()
so.submit()
self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
+
+ stock_ledger_entry = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ ['valuation_rate', 'actual_qty']
+ )
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -1)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ si = make_sales_invoice(dn.name)
+ si.allocate_advances_automatically = 1
+ si.save()
+ si.submit()
+ si.reload()
+ self.assertEqual(si.status, 'Paid')
+
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Debtors - _TC'}, 'debit'), 5200)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': '_Test Account Shipping Charges - _TC'}, 'credit'), 200)
+
+ def test_sales_order_with_partial_advance_payment_and_shipping_rule_TC_S_045(self):
+ so = make_sales_order(
+ cost_center='Main - _TC',
+ selling_price_list='Standard Selling',
+ qty=1,
+ rate=5000,
+ do_not_save=True
+ )
+ so.shipping_rule = "_Test Shipping Rule"
+ so.save()
+ so.submit()
+
+ self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
+ self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+
+ create_registered_bank_account()
+
+ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+
+ pe = get_payment_entry(dt="Sales Order",dn=so.name)
+ pe.reference_no = "1"
+ pe.reference_date = nowdate()
+ pe.paid_amount= 2000
+ for i in pe.references:
+ i.allocated_amount = 2000
+ pe.save()
+ pe.submit()
- def create_payment(allocated_amount, reference_doctype, reference_name):
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
- pe = create_payment_entry(
- company="_Test Indian Registered Company",
- payment_type="Receive",
- party_type="Customer",
- party="_Test Registered Customer",
- paid_from="Debtors - _TIRC",
- paid_to="Cash - _TIRC",
- paid_amount=allocated_amount,
- )
- pe.append('references', {
- "reference_doctype": reference_doctype,
- "reference_name": reference_name,
- "total_amount": so.grand_total,
- "allocated_amount": allocated_amount,
- "account": "Debtors - _TIRC"
- })
- pe.save()
- pe.submit()
- return pe
-
- pe = create_payment(2000, "Sales Order", so.name)
self.assertEqual(pe.status, 'Submitted')
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Debtors - _TIRC'}, 'credit'), 2000)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Cash - _TIRC'}, 'debit'), 2000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Debtors - _TC'}, 'credit'), 2000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Cash - _TC'}, 'debit'), 2000)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
+
+ stock_ledger_entry = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ ['valuation_rate', 'actual_qty']
+ )
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -1)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ si = make_sales_invoice(dn.name)
+ si.allocate_advances_automatically = 1
+ si.save()
+ si.submit()
+ si.reload()
+ self.assertEqual(si.status, 'Partly Paid')
+
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Debtors - _TC'}, 'debit'), 5200)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': '_Test Account Shipping Charges - _TC'}, 'credit'), 200)
+
+ pe2 = get_payment_entry(dt="Sales Invoice",dn=si.name)
+ pe2.reference_no = "1"
+ pe2.reference_date = nowdate()
+ pe2.save()
+ pe2.submit()
+
+ self.assertEqual(pe2.status, 'Submitted')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Debtors - _TC'}, 'credit'), 3200)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Cash - _TC'}, 'debit'), 3200)
+
+ si.reload()
+ self.assertEqual(si.outstanding_amount, 0)
+ self.assertEqual(si.status, "Paid")
+
+ def test_sales_order_create_si_via_pe_dn_with_pricing_rule_TC_S_046(self):
+ make_item_price()
+ make_pricing_rule()
+
+ so = self.create_and_submit_sales_order(qty=10)
+
+ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+
+ pe = get_payment_entry(dt="Sales Order",dn=so.name)
+ pe.save()
+ pe.submit()
+
+ self.assertEqual(pe.status, 'Submitted')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Debtors - _TC'}, 'credit'), 900)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Cash - _TC'}, 'debit'), 900)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ stock_ledger_entry = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ ['valuation_rate', 'actual_qty']
+ )
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -10)
+
+ si = self.create_and_submit_sales_invoice(dn.name,advances_automatically= 1,expected_amount=900)
+ si.reload()
+ self.assertEqual(si.status, "Paid")
+
+ def test_sales_order_create_si_via_partial_pe_with_pricing_rule_TC_S_047(self):
+ make_item_price()
+ make_pricing_rule()
+
+ so = self.create_and_submit_sales_order(qty=10)
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name, amt=400)
+
dn = make_delivery_note(so.name)
dn.submit()
- stock_entry = frappe.get_all('Stock Ledger Entry', {
- 'voucher_no': dn.name,
- 'warehouse': 'Stores - _TIRC',
- 'item_code': '_Test Item'
- }, ['valuation_rate', 'actual_qty'])[0]
- self.assertEqual(stock_entry.get("actual_qty"), -1)
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ stock_ledger_entry = frappe.get_all(
+ 'Stock Ledger Entry',
+ {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ ['valuation_rate', 'actual_qty']
+ )
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -10)
+
+ si = self.create_and_submit_sales_invoice(dn.name,advances_automatically= 1,expected_amount=900)
+ si.reload()
+ self.assertEqual(si.status, "Partly Paid")
+
+ self.create_and_submit_payment_entry(dt="Sales Invoice", dn=si.name)
+
+ si.reload()
+ self.assertEqual(si.status, "Paid")
+
+ def test_sales_order_creating_credit_note_after_SR_TC_S_048(self):
+ so = self.create_and_submit_sales_order(qty=5, rate=3000)
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name)
+
+ dn = self.create_and_validate_delivery_note(so.name, -5)
+
+ si = self.create_and_submit_sales_invoice(dn.name,advances_automatically= 1,expected_amount=15000)
+ si.reload()
+ self.assertEqual(si.status, "Paid")
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+ sr = make_sales_return(dn.name)
+ sr.save()
+ sr.submit()
+
+ self.assertEqual(sr.status, "To Bill", "Sales Return not created")
+
+ qty_change_return = frappe.db.get_value('Stock Ledger Entry', {
+ 'item_code': '_Test Item',
+ 'voucher_no': sr.name,
+ 'warehouse': '_Test Warehouse - _TC'
+ }, 'actual_qty')
+ self.assertEqual(qty_change_return, 5)
+
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return as make_credit_note
+ cn = make_credit_note(si.name)
+ cn.advances.clear()
+ cn.save()
+ cn.submit()
+
+ self.assertEqual(cn.status, "Return", "Credit Note not created")
+
+ voucher_params_cn = {'voucher_type': 'Sales Invoice', 'voucher_no': cn.name}
+ gl_accounts_cn = {'Debtors - _TC': 'credit', 'Sales - _TC': 'debit'}
+ gl_entries_cn = {
+ account: frappe.db.get_value('GL Entry', {**voucher_params_cn, 'account': account}, field)
+ for account, field in gl_accounts_cn.items()
+ }
+ self.assertEqual(gl_entries_cn['Debtors - _TC'], 15000)
+ self.assertEqual(gl_entries_cn['Sales - _TC'], 15000)
+
+ def test_sales_order_creating_full_si_for_service_item_SI_TC_S_050(self):
+ make_service_item()
+
+ so = make_sales_order(cost_center='Main - _TC', selling_price_list='Standard Selling', item_code='Consultancy', qty=1, rate=5000)
+ so.save()
+ so.submit()
+
+ self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name)
+
+ si = make_sales_invoice(so.name)
+ si.allocate_advances_automatically= 1
+ si.only_include_allocated_payments = 1
+ si.save()
+ si.submit()
+ si.reload()
+
+ self.assertEqual(si.status, "Paid", "Sales Invoice not created")
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 5000)
+
+ def test_sales_order_creating_partial_pe_for_service_item_SI_TC_S_051(self):
+ make_service_item()
+
+ so = make_sales_order(cost_center='Main - _TC', selling_price_list='Standard Selling', item_code='Consultancy', qty=1, rate=5000)
+ so.save()
+ so.submit()
- dn_acc_value = stock_entry.get("valuation_rate")
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit'), dn_acc_value)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit'), dn_acc_value)
+ self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name, amt=2000)
+
+ si = make_sales_invoice(so.name)
+ si.allocate_advances_automatically= 1
+ si.only_include_allocated_payments = 1
+ si.save()
+ si.submit()
+ si.reload()
- from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ self.assertEqual(si.status, "Partly Paid", "Sales Invoice not created")
+ self.assertEqual(si.outstanding_amount, 3000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 5000)
+
+ self.create_and_submit_payment_entry(dt="Sales Invoice", dn=si.name)
- si = make_sales_invoice(dn.name)
- si.allocate_advances_automatically = 1
+ si.reload()
+ self.assertEqual(si.status, "Paid")
+ self.assertEqual(si.outstanding_amount, 0)
+
+ def test_sales_order_creating_si_with_update_stock_SI_TC_S_052(self):
+ so = self.create_and_submit_sales_order(qty=1, rate=5000)
+
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name, amt=so.grand_total)
+
+ si = make_sales_invoice(so.name)
+ si.allocate_advances_automatically= 1
+ si.only_include_allocated_payments = 1
+ si.update_stock = 1
si.save()
si.submit()
+ si.reload()
- self.assertEqual(si.status, 'Unpaid')
- self.assertEqual(si.total_advance, 2000)
- self.assertEqual(si.outstanding_amount, 3900)
+ self.assertEqual(si.status, "Paid", "Sales Invoice not created")
+ self.assertEqual(frappe.db.get_value('Stock Ledger Entry', {'voucher_no': si.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ 'actual_qty'), -1)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 5000)
+
+ def test_sales_order_creating_partial_pe_with_update_stock_SI_TC_S_053(self):
+ so = self.create_and_submit_sales_order(qty=1, rate=5000)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit'), 5000)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit'), 5900)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit'), 450)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit'), 450)
+ self.create_and_submit_payment_entry(dt="Sales Order", dn=so.name, amt=2000)
+
+ si = make_sales_invoice(so.name)
+ si.allocate_advances_automatically= 1
+ si.only_include_allocated_payments = 1
+ si.update_stock = 1
+ si.save()
+ si.submit()
+ si.reload()
- pe2 = create_payment(si.outstanding_amount, "Sales Invoice", si.name)
- self.assertEqual(pe2.status, 'Submitted')
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Debtors - _TIRC'}, 'credit'), 3900)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Cash - _TIRC'}, 'debit'), 3900)
+ self.assertEqual(si.status, "Partly Paid", "Sales Invoice not created")
+ self.assertEqual(frappe.db.get_value('Stock Ledger Entry', {'voucher_no': si.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ 'actual_qty'), -1)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 5000)
+
+ self.create_and_submit_payment_entry(dt="Sales Invoice", dn=si.name)
si.reload()
+ self.assertEqual(si.status, "Paid")
self.assertEqual(si.outstanding_amount, 0)
+
+ def test_sales_order_creating_si_with_product_bundle_and_shipping_rule_TC_S_058(self):
+ product_bundle = make_item("_Test Product Bundle", {"is_stock_item": 0})
+ make_item("_Test Bundle Item 1", {"is_stock_item": 1})
+ make_item("_Test Bundle Item 2", {"is_stock_item": 1})
-
- def test_sales_order_with_full_advance_payment_and_shipping_rule_TC_S_044(self):
+ make_product_bundle("_Test Product Bundle", ["_Test Bundle Item 1", "_Test Bundle Item 2"])
+
so = make_sales_order(
cost_center='Main - _TC',
selling_price_list='Standard Selling',
+ item_code=product_bundle.item_code,
qty=1,
- rate=5000,
+ rate=20000,
do_not_save=True
)
so.shipping_rule = "_Test Shipping Rule"
@@ -4231,180 +4332,313 @@ def test_sales_order_with_full_advance_payment_and_shipping_rule_TC_S_044(self):
so.submit()
self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
- self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+ self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
- pe = create_payment_entry(
- payment_type="Receive",
- party_type="Customer",
- party="_Test Customer",
- paid_from="Debtors - _TC",
- paid_to="Cash - _TC",
- paid_amount=so.grand_total,
- )
- pe.append('references', {
- "reference_doctype": "Sales Order",
- "reference_name": so.name,
- "total_amount": so.grand_total,
- "account": "Debtors - _TC",
- })
- pe.save()
- pe.submit()
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ self.assertEqual(frappe.db.get_value('Stock Ledger Entry', {'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Bundle Item 1'}, 'actual_qty'), -1)
+ self.assertEqual(frappe.db.get_value('Stock Ledger Entry', {'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Bundle Item 2'}, 'actual_qty'), -1)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ si = make_sales_invoice(dn.name)
+ si.save()
+ si.submit()
+ si.reload()
+ self.assertEqual(si.status, 'Unpaid')
+
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Sales - _TC'}, 'credit'), 20000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': 'Debtors - _TC'}, 'debit'), 20200)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name,'account': '_Test Account Shipping Charges - _TC'}, 'credit'), 200)
+
+ def test_sales_order_creating_si_with_product_bundle_and_gst_rule_TC_S_059(self):
+ product_bundle = make_item("_Test Product Bundle", {"is_stock_item": 0})
+ item_data = [
+ {"name": "_Test Bundle Item 1", "valuation_rate": 100},
+ {"name": "_Test Bundle Item 2", "valuation_rate": 200}
+ ]
+ for item in item_data:
+ itm = make_item(item["name"], {"is_stock_item": 1})
+ itm.valuation_rate = item["valuation_rate"]
+ itm.save()
+
+ make_product_bundle("_Test Product Bundle", [item["name"] for item in item_data])
+
+ so = self.create_and_submit_sales_order_with_gst(product_bundle.item_code, qty=1, rate=20000)
+
+ dn = make_delivery_note(so.name)
+ dn.submit()
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+ for item in item_data:
+ self.assertEqual(
+ frappe.db.get_value(
+ 'Stock Ledger Entry',
+ {'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC', 'item_code': item["name"]},
+ 'actual_qty'
+ ),
+ -1
+ )
self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Payment Entry',
- 'voucher_no': pe.name,
- 'account': 'Debtors - _TC'
- }, 'credit'),
- so.grand_total
+ frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit'),
+ sum(item["valuation_rate"] for item in item_data)
)
self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Payment Entry',
- 'voucher_no': pe.name,
- 'account': 'Cash - _TC'
- }, 'debit'),
- so.grand_total
+ frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit'),
+ sum(item["valuation_rate"] for item in item_data)
)
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ si = make_sales_invoice(dn.name)
+ si.save()
+ si.submit()
+ si.reload()
+
+ self.assertEqual(si.status, 'Unpaid')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit'), 20000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit'), 23600)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit'), 1800)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit'), 1800)
+
+ def test_sales_order_creating_si_with_installation_note_TC_S_060(self):
+ so = self.create_and_submit_sales_order(qty=5, rate=3000)
+
dn = make_delivery_note(so.name)
dn.submit()
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
stock_ledger_entry = frappe.get_all(
'Stock Ledger Entry',
{'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
['valuation_rate', 'actual_qty']
)
- self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -1)
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -5)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import (make_installation_note, make_sales_invoice)
+ install_note = make_installation_note(dn.name)
+ install_note.inst_date = nowdate()
+ install_note.inst_time = datetime.now().time()
+ install_note.submit()
+ self.assertEqual(install_note.status, "Submitted", "Installation Note not created")
+
+ si = make_sales_invoice(dn.name)
+ si.save()
+ si.submit()
- from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ self.assertEqual(si.status, 'Unpaid')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 15000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 15000)
+
+ return dn, si
+
+ def test_sales_order_creating_returns_with_installation_note_TC_S_061(self):
+ dn, si = self.test_sales_order_creating_si_with_installation_note_TC_S_060()
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+ sr = make_sales_return(dn.name)
+ sr.save()
+ sr.submit()
+
+ self.assertEqual(sr.status, "To Bill", "Sales Return not created")
+
+ qty_change_return = frappe.db.get_value('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': sr.name, 'warehouse': '_Test Warehouse - _TC'}, 'actual_qty')
+ self.assertEqual(qty_change_return, 5)
+
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return as make_credit_note
+ cn = make_credit_note(si.name)
+ cn.save()
+ cn.submit()
+
+ self.assertEqual(cn.status, "Return", "Credit Note not created")
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 15000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 15000)
+
+ def test_sales_order_creating_invoice_with_installation_note_and_gst_TC_S_062(self):
+ so = self.create_and_submit_sales_order_with_gst("_Test Item", qty=5, rate=20)
+
+ dn = make_delivery_note(so.name)
+ dn.save()
+ dn.submit()
+
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+
+ qty_change = frappe.get_all('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': 'Stores - _TIRC'}, ['actual_qty', 'valuation_rate'])
+ self.assertEqual(qty_change[0].get("actual_qty"), -5)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Stock In Hand - _TIRC'}, 'credit'), qty_change[0].get("valuation_rate") * 5)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': dn.name, 'account': 'Cost of Goods Sold - _TIRC'}, 'debit'), qty_change[0].get("valuation_rate") * 5)
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import (make_installation_note, make_sales_invoice)
+ install_note = make_installation_note(dn.name)
+ install_note.inst_date = nowdate()
+ install_note.inst_time = datetime.now().time()
+ install_note.submit()
+ self.assertEqual(install_note.status, "Submitted", "Installation Note not created")
+
si = make_sales_invoice(dn.name)
- si.allocate_advances_automatically = 1
si.save()
si.submit()
- self.assertEqual(si.status, 'Paid')
-
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': 'Sales - _TC'
- }, 'credit'),
- 5000
- )
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': 'Debtors - _TC'
- }, 'debit'),
- 5200
- )
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': '_Test Account Shipping Charges - _TC'
- }, 'credit'),
- 200
+ self.assertEqual(si.status, 'Unpaid')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TIRC'}, 'credit'), 100)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TIRC'}, 'debit'), 118)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax SGST - _TIRC'}, 'credit'), 9)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Output Tax CGST - _TIRC'}, 'credit'), 9)
+
+ def test_sales_order_for_stock_reservation_TC_S_063(self):
+ make_stock_entry(item_code="_Test Item", qty=10, rate=5000, target="_Test Warehouse - _TC")
+
+ stock_setting = frappe.get_doc('Stock Settings')
+ stock_setting.enable_stock_resrvation = 1
+ stock_setting.save()
+
+ so = self.create_and_submit_sales_order(qty=1, rate=5000)
+
+ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import create_stock_reservation_entries_for_so_items
+
+ item_details = [{'__checked': 1, 'sales_order_item': so.items[0].get("name"), 'item_code': '_Test Item',
+ 'warehouse': '_Test Warehouse - _TC', 'qty_to_reserve': 1, 'idx': 1, 'name': 'row 1'}]
+
+ create_stock_reservation_entries_for_so_items(
+ sales_order=so,
+ items_details=item_details,
+ from_voucher_type=None,
+ notify=True,
)
- def test_sales_order_with_partial_advance_payment_and_shipping_rule_TC_S_045(self):
+ self.assertEqual(frappe.db.get_value("Stock Reservation Entry", {"voucher_no": so.name}, "status"), "Reserved")
+
+ dn = make_delivery_note(so.name)
+ dn.save()
+ dn.submit()
+
+ self.assertEqual(dn.status, "To Bill", "Delivery Note not created")
+
+ qty_change = frappe.db.get_value('Stock Ledger Entry', {'item_code': '_Test Item', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC'}, 'actual_qty')
+ self.assertEqual(qty_change, -1)
+
+ self.assertEqual(frappe.db.get_value("Stock Reservation Entry", {"voucher_no": so.name}, "status"), "Delivered")
+
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ si = make_sales_invoice(dn.name)
+ si.save()
+ si.submit()
+
+ self.assertEqual(si.status, 'Unpaid')
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Sales - _TC'}, 'credit'), 5000)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': si.name, 'account': 'Debtors - _TC'}, 'debit'), 5000)
+
+ def create_and_submit_sales_order(self, qty=None, rate=None):
+ sales_order = make_sales_order(cost_center='Main - _TC', selling_price_list='Standard Selling', do_not_save=True)
+ sales_order.delivery_date = nowdate()
+ if qty and rate:
+ for item in sales_order.items:
+ item.qty = qty
+ item.rate = rate
+ sales_order.save()
+ else:
+ for item in sales_order.items:
+ item.qty = qty
+ sales_order.save()
+ sales_order.submit()
+ self.assertEqual(sales_order.status, "To Deliver and Bill")
+ return sales_order
+
+ def create_and_submit_sales_order_with_gst(self, item_code, qty=None, rate=None):
+ company = get_gst_details("Company", {"name": "_Test Indian Registered Company"})[0]
+ customer = get_gst_details("Customer", {"name": "_Test Registered Customer"})[0]
+ company_add = get_gst_details("Address", {"name": "_Test Indian Registered Company-Billing"})[0]
+ customer_add = get_gst_details("Address", {"name": "_Test Registered Customer-Billing"})[0]
+
+ if not (is_registered_regular(company) and is_registered_regular(customer) and
+ is_registered_regular(company_add) and is_registered_regular(customer_add)):
+ self.fail("GST details are not properly configured")
+
so = make_sales_order(
- cost_center='Main - _TC',
- selling_price_list='Standard Selling',
- qty=1,
- rate=5000,
+ company="_Test Indian Registered Company",
+ customer="_Test Registered Customer",
+ warehouse="Stores - _TIRC",
+ cost_center="Main - _TIRC",
+ selling_price_list="Standard Selling",
+ item_code=item_code,
+ qty=qty,
+ rate=rate,
do_not_save=True
)
- so.shipping_rule = "_Test Shipping Rule"
+ so.tax_category = "In-State"
+ so.taxes_and_charges = "Output GST In-state - _TIRC"
+ so.customer_address = customer_add.get("name")
+ so.billing_address_gstin = customer_add.get("gstin")
+ so.company_address = company_add.get("name")
+ so.company_gstin = company_add.get("gstin")
+ for i in so.items:
+ i.gst_hsn_code = "01011020"
so.save()
so.submit()
self.assertEqual(so.status, "To Deliver and Bill", "Sales Order not created")
self.assertEqual(so.grand_total, so.total + so.total_taxes_and_charges)
+
+ return so
- def create_payment(allocated_amount, reference_doctype, reference_name):
- from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
- pe = create_payment_entry(
- company="_Test Company",
- payment_type="Receive",
- party_type="Customer",
- party="_Test Customer",
- paid_from="Debtors - _TC",
- paid_to="Cash - _TC",
- paid_amount=allocated_amount,
- )
- pe.append('references', {
- "reference_doctype": reference_doctype,
- "reference_name": reference_name,
- "total_amount": so.grand_total,
- "allocated_amount": allocated_amount,
- "account": "Debtors - _TIRC"
- })
- pe.save()
- pe.submit()
- return pe
-
- pe = create_payment(2000, "Sales Order", so.name)
- self.assertEqual(pe.status, 'Submitted')
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Debtors - _TC'}, 'credit'), 2000)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe.name, 'account': 'Cash - _TC'}, 'debit'), 2000)
+ def create_and_submit_sales_invoice(self, delivery_note_name, qty=None, expected_amount=None,advances_automatically=None):
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+ sales_invoice = make_sales_invoice(delivery_note_name)
+ sales_invoice.insert()
+ if qty:
+ for item in sales_invoice.items:
+ item.qty = qty
+
+ if advances_automatically:
+ sales_invoice.allocate_advances_automatically= 1
+ sales_invoice.only_include_allocated_payments = 1
+ sales_invoice.save()
+ sales_invoice.submit()
+ if expected_amount:
+ self.validate_gl_entries(sales_invoice.name, expected_amount)
+ return sales_invoice
+
+ def create_and_submit_payment_entry(self, dt=None, dn=None, amt=None):
+ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+ payment_entry = get_payment_entry(dt=dt,dn=dn)
+ payment_entry.insert()
+ if amt:
+ payment_entry.paid_amount= amt
+ for i in payment_entry.references:
+ i.allocated_amount = amt
+ payment_entry.save()
+ payment_entry.submit()
+
+ self.assertEqual(payment_entry.status, "Submitted", "Payment Entry not created")
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': payment_entry.name, 'account': 'Debtors - _TC'}, 'credit'), payment_entry.paid_amount)
+ self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': payment_entry.name, 'account': 'Cash - _TC'}, 'debit'), payment_entry.paid_amount)
+ return payment_entry
- dn = make_delivery_note(so.name)
- dn.submit()
+ def validate_gl_entries(self, voucher_no, amount):
+ debtor_account = frappe.db.get_value("Company", "_Test Company", "default_receivable_account")
+ sales_account = frappe.db.get_value("Company", "_Test Company", "default_income_account")
+ gl_entries = frappe.get_all("GL Entry", filters={"voucher_no": voucher_no}, fields=["account", "debit", "credit"])
+
+ gl_debits = {entry.account: entry.debit for entry in gl_entries}
+ gl_credits = {entry.account: entry.credit for entry in gl_entries}
+
+ self.assertAlmostEqual(gl_debits[debtor_account], amount)
+ self.assertAlmostEqual(gl_credits[sales_account], amount)
+
+ def create_and_validate_delivery_note(self, sales_order_name, expected_amount):
+ delivery_note = make_delivery_note(sales_order_name)
+ delivery_note.submit()
+ self.assertEqual(delivery_note.status, "To Bill", "Delivery Note not created")
stock_ledger_entry = frappe.get_all(
'Stock Ledger Entry',
- {'voucher_type': 'Delivery Note', 'voucher_no': dn.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
+ {'voucher_type': 'Delivery Note', 'voucher_no': delivery_note.name, 'warehouse': '_Test Warehouse - _TC', 'item_code': '_Test Item'},
['valuation_rate', 'actual_qty']
)
- self.assertEqual(stock_ledger_entry[0].get("actual_qty"), -1)
-
- from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
-
- si = make_sales_invoice(dn.name)
- si.allocate_advances_automatically = 1
- si.save()
- si.submit()
-
- self.assertEqual(si.status, 'Partly Paid')
-
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': 'Sales - _TC'
- }, 'credit'),
- 5000
- )
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': 'Debtors - _TC'
- }, 'debit'),
- 5200
- )
- self.assertEqual(
- frappe.db.get_value('GL Entry', {
- 'voucher_type': 'Sales Invoice',
- 'voucher_no': si.name,
- 'account': '_Test Account Shipping Charges - _TC'
- }, 'credit'),
- 200
- )
+ self.assertEqual(stock_ledger_entry[0].get("actual_qty"), expected_amount)
- pe2 = create_payment(si.outstanding_amount, "Sales Invoice", si.name)
- self.assertEqual(pe2.status, 'Submitted')
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Debtors - _TIRC'}, 'credit'), 3900)
- self.assertEqual(frappe.db.get_value('GL Entry', {'voucher_no': pe2.name, 'account': 'Cash - _TIRC'}, 'debit'), 3900)
-
- si.reload()
- self.assertEqual(si.outstanding_amount, 0)
+ return delivery_note
def get_gst_details(doctype, filters):
return frappe.get_all(doctype, filters, ["gstin", "gst_category", "name"])
@@ -4412,6 +4646,17 @@ def get_gst_details(doctype, filters):
def is_registered_regular(details):
return details.get("gst_category") == "Registered Regular" and details.get("gstin")
+def create_registered_bank_account():
+ if not frappe.db.exists('Account', {'name': '_Test Registered Bank Account - _TIRC'}):
+ acc_doc = frappe.new_doc("Account")
+ acc_data = {
+ "company": "_Test Indian Registered Company",
+ "parent_account":"Bank Accounts - _TIRC"
+ }
+ acc_doc.update(acc_data)
+ acc_doc.save()
+ return acc_doc
+
def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.automatically_fetch_payment_terms = enable
@@ -4425,7 +4670,6 @@ def compare_payment_schedules(doc, doc1, doc2):
doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion)
doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount)
-
def make_sales_order(**args):
so = frappe.new_doc("Sales Order")
args = frappe._dict(args)
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index d8d97fb4fdb7..d1b6cca4146f 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -58,13 +58,16 @@ def search_by_term(search_term, warehouse, price_list):
item_stock_qty = item_stock_qty // item.get("conversion_factor", 1)
item.update({"actual_qty": item_stock_qty})
+ price_filters = {
+ "price_list": price_list,
+ "item_code": item_code,
+ }
+ if batch_no:
+ price_filters["batch_no"] = batch_no
+
price = frappe.get_list(
doctype="Item Price",
- filters={
- "price_list": price_list,
- "item_code": item_code,
- "batch_no": batch_no,
- },
+ filters=price_filters,
fields=["uom", "currency", "price_list_rate", "batch_no"],
)
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 0017b8cfab04..a382b175b313 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -928,10 +928,13 @@ erpnext.PointOfSale.ItemCart = class {
const me = this;
dfs.forEach((df) => {
this[`customer_${df.fieldname}_field`] = frappe.ui.form.make_control({
- df: { ...df, onchange: handle_customer_field_change },
+ df: df,
parent: $customer_form.find(`.${df.fieldname}-field`),
render_input: true,
});
+ this[`customer_${df.fieldname}_field`].$input?.on("blur", () => {
+ handle_customer_field_change.apply(this[`customer_${df.fieldname}_field`]);
+ });
this[`customer_${df.fieldname}_field`].set_value(this.customer_info[df.fieldname]);
});
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index af1beab5304c..f2f85122edc5 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -210,10 +210,21 @@ erpnext.PointOfSale.ItemDetails = class {
make_auto_serial_selection_btn(item) {
if (item.has_serial_no || item.has_batch_no) {
- const label = item.has_serial_no ? __("Select Serial No") : __("Select Batch No");
- this.$form_container.append(
- `${label}
`
- );
+ if (item.has_serial_no && item.has_batch_no) {
+ this.$form_container.append(
+ `${__(
+ "Select Serial No / Batch No"
+ )}
`
+ );
+ } else {
+ const classname = item.has_serial_no ? ".serial_no-control" : ".batch_no-control";
+ const label = item.has_serial_no ? __("Select Serial No") : __("Select Batch No");
+ this.$form_container
+ .find(classname)
+ .append(
+ `${label}
`
+ );
+ }
this.$form_container.find(".serial_no-control").find("textarea").css("height", "6rem");
}
}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 0b3bf9696cfc..c72e94e1cb65 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -328,13 +328,15 @@ erpnext.PointOfSale.ItemSelector = class {
}
filter_items({ search_term = "" } = {}) {
+ const selling_price_list = this.events.get_frm().doc.selling_price_list;
if (search_term) {
search_term = search_term.toLowerCase();
// memoize
this.search_index = this.search_index || {};
- if (this.search_index[search_term]) {
- const items = this.search_index[search_term];
+ this.search_index[selling_price_list] = this.search_index[selling_price_list] || {};
+ if (this.search_index[selling_price_list][search_term]) {
+ const items = this.search_index[selling_price_list][search_term];
this.items = items;
this.render_item_list(items);
this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart();
@@ -346,7 +348,7 @@ erpnext.PointOfSale.ItemSelector = class {
// eslint-disable-next-line no-unused-vars
const { items, serial_no, batch_no, barcode } = message;
if (search_term && !barcode) {
- this.search_index[search_term] = items;
+ this.search_index[selling_price_list][search_term] = items;
}
this.items = items;
this.render_item_list(items);
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 7233fc97114c..acbea2ce53a8 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -820,10 +820,6 @@
"read": 1,
"role": "Stock User"
},
- {
- "read": 1,
- "role": "Projects User"
- },
{
"create": 1,
"delete": 1,
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 267173845a12..90bf4829fc15 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -43,6 +43,14 @@ frappe.ui.form.on("Material Request", {
};
});
+ frm.set_query("project", "items", function (doc, cdt, cdn) {
+ return {
+ filters: {
+ company: doc.company,
+ },
+ };
+ });
+
var list = frm.fields_dict['items'].grid.get_field('work_breakdown_structure').get_query = function (doc, cdt, cdn) {
var child = locals[cdt][cdn];
return {
diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py
index fa8286bf8e95..17b3036a13ed 100644
--- a/erpnext/stock/doctype/material_request/test_material_request.py
+++ b/erpnext/stock/doctype/material_request/test_material_request.py
@@ -2912,6 +2912,81 @@ def test_create_material_req_to_2po_to_1pr_cancel_TC_SCK_058(self):
sle = frappe.get_doc('Stock Ledger Entry',{'voucher_no':pr.name})
self.assertEqual(sle.qty_after_transaction, 0)
+ def test_create_mr_issue_to_stock_entry_with_batch_and_TC_SCK_062(self):
+ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry as _make_stock_entry
+
+ fields = {
+ "has_batch_no": 1,
+ "is_stock_item": 1,
+ "create_new_batch": 1,
+ "batch_naming_series": "Test-SBBTYT-NNS.#####",
+ }
+
+ if frappe.db.has_column("Item", "gst_hsn_code"):
+ fields["gst_hsn_code"] = "01011010"
+
+ company = "_Test Company"
+ qty = 10
+ frappe.db.set_value("Company", "_Test Company", "enable_perpetual_inventory", 1)
+ frappe.db.set_value("Company", "_Test Company", "stock_adjustment_account", "Stock Adjustment - _TC")
+ target_warehouse = create_warehouse("_Test Warehouse", properties=None, company=company)
+ item = make_item("Test Use Serial and Batch Item SN Item", fields).name
+
+ new_stock = _make_stock_entry(
+ item_code=item,
+ qty=10,
+ to_warehouse=target_warehouse,
+ company="_Test Company",
+ rate=100,
+ )
+ self.assertTrue(new_stock.items[0].serial_and_batch_bundle)
+
+ mr = make_material_request(
+ material_request_type="Material Issue", qty=qty, warehouse=target_warehouse, item_code=item
+ )
+ self.assertEqual(mr.status, "Pending")
+
+ bin_qty = (
+ frappe.db.get_value("Bin", {"item_code": item, "warehouse": target_warehouse}, "actual_qty") or 0
+ )
+ stock_in_hand_account = get_inventory_account(company, target_warehouse)
+
+ # Make stock entry against material request issue
+ se = make_stock_entry(mr.name)
+ se.items[0].expense_account = "Cost of Goods Sold - _TC"
+ se.serial_and_batch_bundle = new_stock.items[0].serial_and_batch_bundle
+ se.insert()
+ se.submit()
+ mr.load_from_db()
+ self.assertEqual(mr.status, "Issued")
+
+ sle = frappe.get_doc("Stock Ledger Entry", {"voucher_no": se.name})
+ stock_value_diff = abs(
+ frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_type": "Stock Entry", "voucher_no": se.name},
+ "stock_value_difference",
+ )
+ )
+ gle = get_gle(company, se.name, stock_in_hand_account)
+ gle1 = get_gle(company, se.name, "Cost of Goods Sold - _TC")
+ self.assertEqual(sle.qty_after_transaction, bin_qty - qty)
+ self.assertEqual(gle[1], stock_value_diff)
+ self.assertEqual(gle1[0], stock_value_diff)
+ se.cancel()
+ mr.load_from_db()
+
+ # After stock entry cancel
+ current_bin_qty = (
+ frappe.db.get_value("Bin", {"item_code": item, "warehouse": target_warehouse}, "actual_qty") or 0
+ )
+ sh_gle = get_gle(company, se.name, stock_in_hand_account)
+ cogs_gle = get_gle(company, se.name, "Cost of Goods Sold - _TC")
+
+ self.assertEqual(sh_gle[0], sh_gle[1])
+ self.assertEqual(cogs_gle[0], cogs_gle[1])
+ self.assertEqual(current_bin_qty, bin_qty)
+
def test_mr_transfer_to_se_cancel_TC_SCK_061(self):
source_wh = create_warehouse(
warehouse_name="_Test Source Warehouse",
@@ -2978,6 +3053,1210 @@ def test_mr_po_pi_TC_SCK_082(self):
gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
self.assertEqual(gl_stock_debit, 1000)
+ def test_mr_po_2pi_TC_SCK_083(self):
+ # MR => PO => 2PI
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.items[0].qty = 5
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.items[0].qty = 5
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_2pi_TC_SCK_084(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr.submit()
+ pr1 = create_purchase_invoice(po1.name)
+ pr1.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_1pi_TC_SCK_085(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr = create_purchase_invoice(po1.name, target_doc=pr)
+ pr.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_pi_cancel_TC_SCK_086(self):
+ # MR => PO => PI => PI Cancel
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_pi.reload()
+ doc_pi.load_from_db()
+ self.assertEqual(doc_pi.status, "Unpaid")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ doc_pi.cancel()
+ doc_pi.reload()
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_2pi_cancel_TC_SCK_087(self):
+ # MR => PO => 2PI => 2PI cancel
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.items[0].qty = 5
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pi1 = create_purchase_invoice(doc_po.name)
+ doc_pi1.items[0].qty = 5
+ doc_pi1.submit()
+
+ self.assertEqual(doc_pi1.docstatus, 1)
+ doc_pi1.reload()
+ self.assertEqual(doc_pi1.status, "Unpaid")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #cancel PI's
+ doc_pi.reload()
+ doc_pi.cancel()
+ doc_pi.reload()
+ self.assertEqual(doc_pi.status, "Cancelled")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pi1.cancel()
+ doc_pi1.reload()
+ self.assertEqual(doc_pi1.status, "Cancelled")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_2pi_cancel_TC_SCK_088(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr.submit()
+ pr1 = create_purchase_invoice(po1.name)
+ pr1.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #cancel PI's
+ pr.reload()
+ pr.cancel()
+ pr.reload()
+ self.assertEqual(pr.status, "Cancelled")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ pr1.reload()
+ pr1.cancel()
+ pr1.reload()
+ self.assertEqual(pr1.status, "Cancelled")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_mr_material_transfer_to_stock_entry_TC_SCK_064(self):
+ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry as _make_stock_entry
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import TestStockEntry as tse
+
+ item = create_item("_Test Item")
+ source_warehouse = create_warehouse(
+ "_Test Source Warehouse", properties=None, company="_Test Company"
+ )
+ t_warehouse = create_warehouse(
+ warehouse_name="_Test Warehouse 1", properties=None, company="_Test Company"
+ )
+ _make_stock_entry(
+ item_code=item.name,
+ qty=10,
+ to_warehouse=source_warehouse,
+ company="_Test Company",
+ rate=120,
+ )
+ s_bin_qty = (
+ frappe.db.get_value("Bin", {"item_code": item.name, "warehouse": source_warehouse}, "actual_qty")
+ or 0
+ )
+
+ # Create Material Request for Material Transfer
+ mr = make_material_request(
+ material_request_type="Material Transfer",
+ qty=10,
+ warehouse=t_warehouse,
+ from_warehouse=source_warehouse,
+ item_code=item.name,
+ )
+ self.assertEqual(mr.status, "Pending")
+
+ # Create Stock Entry based on Material Request
+ se = make_stock_entry(mr.name)
+ se.save()
+ se.submit()
+ tse.check_stock_ledger_entries(
+ self,
+ "Stock Entry",
+ se.name,
+ [
+ [item.name, t_warehouse, 10],
+ [item.name, source_warehouse, -10],
+ ],
+ )
+ mr.load_from_db()
+ self.assertEqual(mr.status, "Transferred")
+
+ # Cancel Stock Entry and check qty in source warehouse
+ se.cancel()
+ mr.load_from_db()
+ current_qty = (
+ frappe.db.get_value("Bin", {"item_code": item.name, "warehouse": source_warehouse}, "actual_qty")
+ or 0
+ )
+ self.assertEqual(current_qty, s_bin_qty)
+ self.assertEqual(mr.status, "Pending")
+
+ def test_create_mr_for_purchase_to_po_TC_SCK_019(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import TestStockEntry as tse
+
+ # Create Material Request for Purchase
+ fields = {
+ "has_batch_no": 1,
+ "has_serial_no": 1,
+ "is_stock_item": 1,
+ "create_new_batch": 1,
+ "batch_naming_series": "Test-SABBMRP-Bno.#####",
+ }
+
+ if frappe.db.has_column("Item", "gst_hsn_code"):
+ fields["gst_hsn_code"] = "01011010"
+
+ item = make_item("Test Use Serial and Batch Item SN Item", fields).name
+ mr = make_material_request(
+ material_request_type="Purchase",
+ qty=2,
+ item_code=item,
+ rate=10000
+ )
+
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.save()
+ po.submit()
+
+ pr = make_purchase_receipt(po.name)
+ pr.items[0].use_serial_batch_fields = 1
+ pr.items[0].serial_no = "Test-SABBMRP-Sno-001\nTest-SABBMRP-Sno-002"
+
+ if not frappe.db.exists({"doctype": "Batch", "batch_id":"Test-SABBMRP-Bno-001"}):
+ b_no = frappe.new_doc("Batch")
+ b_no.batch_id = "Test-SABBMRP-Bno-001"
+ b_no.item = item
+ b_no.save()
+
+ pr.items[0].batch_no = "Test-SABBMRP-Bno-001"
+ pr.save()
+ pr.submit()
+
+ sl_entry = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
+ ["actual_qty", "serial_and_batch_bundle"],
+ order_by="creation",
+ )
+
+ sabb = frappe.get_doc("Serial and Batch Bundle", sl_entry[0].serial_and_batch_bundle)
+ self.assertEqual(sl_entry[0].actual_qty, 2)
+ self.assertEqual(sabb.entries[0].serial_no, "Test-SABBMRP-Sno-001")
+ self.assertEqual(sabb.entries[1].serial_no, "Test-SABBMRP-Sno-002")
+ self.assertEqual(sabb.entries[0].batch_no, "Test-SABBMRP-Bno-001")
+
+ def test_create_mr_for_purchase_to_po_2pr_TC_SCK_020(self):
+ fields = {
+ "has_batch_no": 1,
+ "has_serial_no": 1,
+ "is_stock_item": 1,
+ "create_new_batch": 1,
+ "batch_naming_series": "Test-SABBMRP-Bno.#####",
+ }
+
+ if frappe.db.has_column("Item", "gst_hsn_code"):
+ fields["gst_hsn_code"] = "01011010"
+
+ item = make_item("Test Use Serial and Batch Item SN Item", fields).name
+
+ # Create Material Request for Purchase
+ mr = make_material_request(
+ material_request_type="Purchase",
+ qty=5,
+ item_code=item,
+ rate=10000,
+ do_not_submit=True
+ )
+ mr.transaction_date = "01-08-2024"
+ mr.schedule_date = "15-08-2024"
+ mr.save()
+ mr.submit()
+
+ po = make_purchase_order(mr.name)
+ po.posting_date = "05-08-2024"
+ po.supplier = "_Test Supplier"
+ po.save()
+ po.submit()
+
+ pr1 = make_purchase_receipt(po.name)
+ pr1.posting_date = "05-08-2024"
+ pr1.items[0].use_serial_batch_fields = 1
+ pr1.items[0].qty = 3
+ pr1.items[0].serial_no = "Test-SABBMRP-Sno-001\nTest-SABBMRP-Sno-002\nTest-SABBMRP-Sno-003"
+
+ if not frappe.db.exists({"doctype": "Batch", "batch_id":"Test-SABBMRP-Bno-001"}):
+ b_no = frappe.new_doc("Batch")
+ b_no.batch_id = "Test-SABBMRP-Bno-001"
+ b_no.item = item
+ b_no.save()
+
+ pr1.items[0].batch_no = "Test-SABBMRP-Bno-001"
+ pr1.save()
+ pr1.submit()
+
+ sl_entry = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name},
+ ["actual_qty", "serial_and_batch_bundle"],
+ order_by="creation",
+ )
+
+ sabb = frappe.get_doc("Serial and Batch Bundle", sl_entry[0].serial_and_batch_bundle)
+ self.assertEqual(sl_entry[0].actual_qty, 3)
+ self.assertEqual(sabb.entries[0].serial_no, "Test-SABBMRP-Sno-001")
+ self.assertEqual(sabb.entries[0].batch_no, "Test-SABBMRP-Bno-001")
+
+ pr2 = make_purchase_receipt(po.name)
+ pr2.posting_date = "10-08-2024"
+ pr2.items[0].use_serial_batch_fields = 1
+ pr2.items[0].qty = 2
+ pr2.items[0].serial_no = "Test-SABBMRP-Sno-004\nTest-SABBMRP-Sno-005"
+
+ if not frappe.db.exists({"doctype": "Batch", "batch_id":"Test-SABBMRP-Bno-001"}):
+ b_no = frappe.new_doc("Batch")
+ b_no.batch_id = "Test-SABBMRP-Bno-001"
+ b_no.item = item
+ b_no.save()
+
+ pr2.items[0].batch_no = "Test-SABBMRP-Bno-001"
+ pr2.save()
+ pr2.submit()
+
+ sl_entry = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {"voucher_type": "Purchase Receipt", "voucher_no": pr2.name},
+ ["actual_qty", "serial_and_batch_bundle"],
+ order_by="creation",
+ )
+
+ sabb = frappe.get_doc("Serial and Batch Bundle", sl_entry[0].serial_and_batch_bundle)
+ self.assertEqual(sl_entry[0].actual_qty, 2)
+ self.assertEqual(sabb.entries[1].serial_no, "Test-SABBMRP-Sno-005")
+ self.assertEqual(sabb.entries[1].batch_no, "Test-SABBMRP-Bno-001")
+
+
+ def test_create_material_req_to_2po_to_1pi_cancel_TC_SCK_089(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr = create_purchase_invoice(po1.name, target_doc=pr)
+ pr.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ pr.reload()
+ pr.cancel()
+ pr.reload()
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Creditors - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_pi_return_TC_SCK_090(self):
+ # MR => PO => PI => Return
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ doc_pi.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", doc_pi.name)
+ return_pi.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_2pi_return_TC_SCK_101(self):
+ # MR => PO => 2PI => 2PI return
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.items[0].qty = 5
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pi1 = create_purchase_invoice(doc_po.name)
+ doc_pi1.items[0].qty = 5
+ doc_pi1.submit()
+
+ self.assertEqual(doc_pi1.docstatus, 1)
+ doc_pi1.reload()
+ self.assertEqual(doc_pi1.status, "Unpaid")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #Return PI's
+ doc_pi.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", doc_pi.name)
+ return_pi.submit()
+ return_pi = make_return_doc("Purchase Invoice", doc_pi1.name)
+ return_pi.submit()
+
+ doc_pi.reload()
+ doc_pi1.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_2pi_return_TC_SCK_102(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr.submit()
+ pr1 = create_purchase_invoice(po1.name)
+ pr1.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #Return PI's
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", pr.name)
+ return_pi.submit()
+ return_pi1 = make_return_doc("Purchase Invoice", pr1.name)
+ return_pi1.submit()
+
+ pr.reload()
+ pr1.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_1pi_return_TC_SCK_103(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr = create_purchase_invoice(po1.name, target_doc=pr)
+ pr.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", pr.name)
+ return_pi.submit()
+ pr.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock In Hand - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_pi_partial_return_TC_SCK_104(self):
+ # MR => PO => PI => Return
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ doc_pi.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", doc_pi.name)
+ return_pi.get("items")[0].qty = -5
+ return_pi.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_mr_po_2pi_partial_return_TC_SCK_105(self):
+ # MR => PO => 2PI => 2PI return
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pi = create_purchase_invoice(doc_po.name)
+ doc_pi.items[0].qty = 5
+ doc_pi.submit()
+
+ self.assertEqual(doc_pi.docstatus, 1)
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Ordered")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pi1 = create_purchase_invoice(doc_po.name)
+ doc_pi1.items[0].qty = 5
+ doc_pi1.submit()
+
+ self.assertEqual(doc_pi1.docstatus, 1)
+ doc_pi1.reload()
+ self.assertEqual(doc_pi1.status, "Unpaid")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #Return PI's
+ doc_pi.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", doc_pi.name)
+ return_pi.submit()
+
+ doc_pi.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pi.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_material_req_to_2po_to_1pr_return_TC_SCK_036(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = make_purchase_receipt(po.name)
+ pr = make_purchase_receipt(po1.name, target_doc=pr)
+ pr.submit()
+
+ bin_qty = frappe.db.get_value("Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "actual_qty")
+ sle = frappe.get_doc('Stock Ledger Entry',{'voucher_no':pr.name})
+ self.assertEqual(sle.qty_after_transaction, bin_qty)
+ self.assertEqual(sle.warehouse, mr.get("items")[0].warehouse)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ #Return PI's
+ pr.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Receipt", pr.name)
+ return_pi.submit()
+
+ pr.reload()
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock In Hand - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_mr_po_pr_partial_return_TC_SCK_038(self):
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "_Test Item",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pr = make_test_pr(doc_po.name)
+
+ doc_mr.reload()
+ self.assertEqual(doc_mr.status, "Received")
+ doc_pr.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Receipt", doc_pr.name)
+ return_pi.get("items")[0].rejected_qty = -5
+ return_pi.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':return_pi.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_mr_po_2pr_partial_return_TC_SCK_041(self):
+ # MR => PO => 2PR => PR return
+ mr_dict_list = [{
+ "company" : "_Test Company",
+ "item_code" : "Testing-31",
+ "warehouse" : "Stores - _TC",
+ "qty" : 10,
+ "rate" : 100,
+ },
+ ]
+
+ doc_mr = make_material_request(**mr_dict_list[0])
+ self.assertEqual(doc_mr.docstatus, 1)
+
+ doc_po = make_test_po(doc_mr.name)
+ doc_pr = make_test_pr(doc_po.name,received_qty = 5)
+ doc_pr.submit()
+
+ doc_mr.reload()
+ self.assertEqual(doc_pr.status, "To Bill")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ doc_pr1 = make_test_pr(doc_po.name)
+ doc_pr1.items[0].accepted_qty = 5
+ doc_pr1.submit()
+
+ self.assertEqual(doc_pr1.docstatus, 1)
+ doc_pr1.reload()
+ self.assertEqual(doc_pr1.status, "To Bill")
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr1.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr1.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #Return PI's
+ doc_pr.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pr = make_return_doc("Purchase Receipt", doc_pr.name)
+ return_pr.get("items")[0].received_qty = -5
+ return_pr.submit()
+
+ doc_pr.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':doc_pr.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ def test_create_mr_to_2po_to_1pr_part_return_TC_SCK_042(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = make_purchase_receipt(po.name)
+ pr = make_purchase_receipt(po1.name, target_doc=pr)
+ pr.submit()
+
+ bin_qty = frappe.db.get_value("Bin", {"item_code": "_Test Item", "warehouse": "_Test Warehouse - _TC"}, "actual_qty")
+ sle = frappe.get_doc('Stock Ledger Entry',{'voucher_no':pr.name})
+ self.assertEqual(sle.qty_after_transaction, bin_qty)
+ self.assertEqual(sle.warehouse, mr.get("items")[0].warehouse)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ #Return PI's
+ pr.load_from_db()
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Receipt", pr.name)
+ return_pi.items.pop(0)
+ return_pi.items[0].received_qty = -5
+ return_pi.submit()
+
+ pr.reload()
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'credit')
+ self.assertEqual(gl_temp_credit, 1000)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock In Hand - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock In Hand - _TC'},'debit')
+ self.assertEqual(gl_stock_debit, 1000)
+
+ def test_create_mr_to_2po_to_2pi_partial_return_TC_SCK_106(self):
+ mr = make_material_request()
+
+ #partially qty
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].rate = 100
+ po.get("items")[0].qty = 5
+ po.insert()
+ po.submit()
+
+ #remaining qty
+ po1 = make_purchase_order(mr.name)
+ po1.supplier = "_Test Supplier"
+ po1.get("items")[0].rate = 100
+ po1.get("items")[0].qty = 5
+ po1.insert()
+ po1.submit()
+
+ pr = create_purchase_invoice(po.name)
+ pr.submit()
+ pr1 = create_purchase_invoice(po1.name)
+ pr1.submit()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ recive_account = frappe.db.get_value("Company",mr.company,"stock_received_but_not_billed")
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': recive_account},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ payable_act = frappe.db.get_value("Company",mr.company,"default_payable_account")
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': payable_act},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
+ #Return PI's
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ return_pi = make_return_doc("Purchase Invoice", pr.name)
+ return_pi.submit()
+
+ pr.reload()
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Stock Received But Not Billed - _TC'}):
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+ gl_temp_credit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Stock Received But Not Billed - _TC'},'debit')
+ self.assertEqual(gl_temp_credit, 500)
+
+ #if account setup in company
+ if frappe.db.exists('GL Entry',{'account': 'Creditors - _TC'}):
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+ gl_stock_debit = frappe.db.get_value('GL Entry',{'voucher_no':pr1.name, 'account': 'Creditors - _TC'},'credit')
+ self.assertEqual(gl_stock_debit, 500)
+
def get_in_transit_warehouse(company):
if not frappe.db.exists("Warehouse Type", "Transit"):
frappe.get_doc(
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index ef74ec9d6274..816b2260c266 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -20,6 +20,7 @@
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import (
EmptyStockReconciliationItemsError,
)
+from frappe.utils import add_days, add_months, flt, getdate, nowdate
test_dependencies = ["Item", "Sales Invoice", "Stock Entry", "Batch"]
@@ -1266,3 +1267,196 @@ def test_ignore_pricing_rule_in_pick_list(self):
delivery_note = create_delivery_note(pl.name)
self.assertEqual(len(delivery_note.items), 1)
+
+
+ def test_quotation_to_sales_invoice_with_pick_list_TC_S_085(self):
+ from erpnext.selling.doctype.quotation.quotation import make_sales_order
+ from erpnext.selling.doctype.quotation.test_quotation import make_quotation
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ make_stock_entry(item="_Test Item Home Desktop 100", target="Stores - _TC", qty=10, rate=4000)
+ quotation = make_quotation(
+ item="_Test Item Home Desktop 100",
+ qty=4,
+ rate=5000,
+ warehouse="Stores - _TC",
+ )
+ quotation.save()
+ quotation.submit()
+ self.assertEqual(quotation.status, "Open")
+
+ sales_order = make_sales_order(quotation.name)
+ sales_order.delivery_date = add_days(nowdate(), 5)
+ sales_order.insert()
+ sales_order.submit()
+
+ self.assertEqual(sales_order.status, "To Deliver and Bill")
+ quotation.reload()
+ self.assertEqual(quotation.status, "Ordered")
+ # Pick list
+ pick_list = create_pick_list(sales_order.name)
+ pick_list.save()
+ pick_list.submit()
+ # Delivery note
+ delivery_note = create_delivery_note(pick_list.name)
+ delivery_note.save()
+ delivery_note.submit()
+
+ stock_check(self,delivery_note.name,-4)
+
+ # sales invoice
+ sales_invoice = make_sales_invoice(delivery_note.name)
+ sales_invoice.insert()
+ sales_invoice.submit()
+ validate_gl_entries(self, sales_invoice.name, 20000)
+
+ def test_sales_order_to_sales_invoice_with_pick_list_TC_S_086(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ make_stock_entry(item="_Test Item Home Desktop 100", target="Stores - _TC", qty=5, rate=4000)
+
+ sales_order = make_sales_order(item_code="_Test Item Home Desktop 100", qty=4, rate=5000)
+ self.assertEqual(sales_order.status, "To Deliver and Bill")
+
+ # Pick list
+ pick_list = create_pick_list(sales_order.name)
+ pick_list.save()
+ pick_list.submit()
+ # Delivery note
+ delivery_note = create_delivery_note(pick_list.name)
+ delivery_note.save()
+ delivery_note.submit()
+
+ stock_check(self,delivery_note.name,-4)
+
+ # sales invoice
+ sales_invoice = make_sales_invoice(delivery_note.name)
+ sales_invoice.insert()
+ sales_invoice.submit()
+ validate_gl_entries(self, sales_invoice.name, 20000)
+
+ def test_sales_order_to_sales_invoice_with_double_entries_TC_S_087(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ make_stock_entry(item="_Test Item Home Desktop 100", target="Stores - _TC", qty=5, rate=4000)
+
+ sales_order = make_sales_order(item_code="_Test Item Home Desktop 100", qty=4, rate=5000)
+ self.assertEqual(sales_order.status, "To Deliver and Bill")
+
+ # Pick list
+ pick_list_1 = create_pick_list(sales_order.name)
+ pick_list_1.save()
+ for i in pick_list_1.locations:
+ i.qty = 2
+ i.stock_qty = 2
+ pick_list_1.submit()
+ # Delivery note
+ delivery_note_1 = create_delivery_note(pick_list_1.name)
+ delivery_note_1.save()
+ delivery_note_1.submit()
+
+ stock_check(self,delivery_note_1.name,-2)
+
+ # sales invoice
+ sales_invoice_1 = make_sales_invoice(delivery_note_1.name)
+ sales_invoice_1.insert()
+ sales_invoice_1.submit()
+ validate_gl_entries(self, sales_invoice_1.name, 10000)
+
+ delivery_note_1.reload()
+ self.assertEqual(sales_invoice_1.status, "Unpaid")
+ self.assertEqual(delivery_note_1.status, "Completed")
+
+ # Pick list
+ pick_list_2 = create_pick_list(sales_order.name)
+ pick_list_2.save()
+ for i in pick_list_2.locations:
+ i.qty = 2
+ i.stock_qty = 2
+ pick_list_2.submit()
+ # Delivery note
+ delivery_note_2 = create_delivery_note(pick_list_2.name)
+ delivery_note_2.save()
+ delivery_note_2.submit()
+
+ stock_check(self,delivery_note_2.name,-2)
+
+ # sales invoice
+ sales_invoice_2 = make_sales_invoice(delivery_note_2.name)
+ sales_invoice_2.insert()
+ sales_invoice_2.submit()
+ validate_gl_entries(self, sales_invoice_2.name, 10000)
+
+ sales_order.reload()
+ delivery_note_2.reload()
+ self.assertEqual(sales_invoice_2.status, "Unpaid")
+ self.assertEqual(sales_order.status, "Completed")
+ self.assertEqual(delivery_note_2.status, "Completed")
+
+
+ def test_sales_order_to_sales_invoice_with_2_SI_TC_S_088(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+ from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
+
+ make_stock_entry(item="_Test Item Home Desktop 100", target="Stores - _TC", qty=5, rate=4000)
+
+ sales_order = make_sales_order(item_code="_Test Item Home Desktop 100", qty=4, rate=5000)
+ self.assertEqual(sales_order.status, "To Deliver and Bill")
+
+ # Pick list
+ pick_list = create_pick_list(sales_order.name)
+ pick_list.save()
+ pick_list.submit()
+ # Delivery note
+ delivery_note = create_delivery_note(pick_list.name)
+ delivery_note.save()
+ delivery_note.submit()
+
+ stock_check(self,delivery_note.name,-4)
+ self.assertEqual(delivery_note.status, "To Bill")
+
+
+ # sales invoice
+ sales_invoice_1 = make_sales_invoice(delivery_note.name)
+ for i in sales_invoice_1.items:
+ i.qty=2
+ sales_invoice_1.insert()
+ sales_invoice_1.submit()
+ validate_gl_entries(self, sales_invoice_1.name, 10000)
+ self.assertEqual(sales_invoice_1.status, "Unpaid")
+
+
+ sales_invoice_2 = make_sales_invoice(delivery_note.name)
+ for i in sales_invoice_2.items:
+ i.qty=2
+ sales_invoice_2.insert()
+ sales_invoice_2.submit()
+ validate_gl_entries(self, sales_invoice_2.name, 10000)
+ self.assertEqual(sales_invoice_2.status, "Unpaid")
+
+ sales_order.reload()
+ delivery_note.reload()
+ self.assertEqual(sales_order.status, "Completed")
+ self.assertEqual(delivery_note.status, "Completed")
+
+def stock_check(self,voucher,qty):
+ stock_entries = frappe.get_all(
+ "Stock Ledger Entry",
+ filters={"voucher_no":voucher, "warehouse": "Stores - _TC"},
+ fields=["actual_qty"]
+ )
+ self.assertEqual(sum([entry.actual_qty for entry in stock_entries]), qty)
+
+def validate_gl_entries(self, voucher_no, amount):
+ debtor_account = frappe.db.get_value("Company", "_Test Company", "default_receivable_account")
+ sales_account = frappe.db.get_value("Company", "_Test Company", "default_income_account")
+ gl_entries = frappe.get_all("GL Entry", filters={"voucher_no": voucher_no}, fields=["account", "debit", "credit"])
+
+ gl_debits = {entry.account: entry.debit for entry in gl_entries}
+ gl_credits = {entry.account: entry.credit for entry in gl_entries}
+
+ self.assertAlmostEqual(gl_debits[debtor_account], amount)
+ self.assertAlmostEqual(gl_credits[sales_account], amount)
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 5ba8c280a862..9c1b1528f863 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -4345,6 +4345,48 @@ def check_gl_entry(self, doc, expected_values):
self.assertEqual(expected_values[gle.account][0], gle.debit)
self.assertEqual(expected_values[gle.account][1], gle.credit)
+ def test_pr_with_additional_discount_TC_B_056(self):
+ company = "_Test Company"
+ item_code = "Testing-31"
+ target_warehouse = "Stores - _TC"
+ supplier = "_Test Supplier 1"
+ item_price = 10000
+ if not frappe.db.exists("Item", item_code):
+ frappe.get_doc({
+ "doctype": "Item",
+ "item_code": item_code,
+ "item_name": item_code,
+ "is_stock_item": 1,
+ "is_purchase_item": 1,
+ "is_sales_item": 0,
+ "company": company
+ }).insert()
+ pi = frappe.get_doc({
+ "doctype": "Purchase Receipt",
+ "supplier": supplier,
+ "company": company,
+ "posting_date": today(),
+ "set_warehouse": target_warehouse,
+ "items": [
+ {
+ "item_code": item_code,
+ "warehouse": target_warehouse,
+ "qty": 1,
+ "rate": item_price
+ }
+ ]
+ })
+ pi.insert()
+ self.assertEqual(len(pi.items), 1)
+ self.assertEqual(pi.items[0].rate, item_price)
+ self.assertEqual(pi.net_total, item_price)
+ pi.apply_discount_on = "Net Total"
+ pi.additional_discount_percentage = 10
+ pi.save()
+ self.assertEqual(pi.discount_amount, 1000)
+ self.assertEqual(pi.net_total, 9000)
+ pi.submit()
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index f1a8ace7ba41..3fdd103d2c69 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1562,7 +1562,7 @@ def get_type_of_transaction(parent_doc, child_row):
elif parent_doc.get("doctype") == "Stock Reconciliation":
type_of_transaction = "Inward"
- if parent_doc.get("is_return"):
+ if parent_doc.get("is_return") and parent_doc.get("doctype") != "Stock Entry":
type_of_transaction = "Inward"
if (
parent_doc.get("doctype") in ["Purchase Receipt", "Purchase Invoice"]
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 78dde44f7446..7a3a16a11b49 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -200,7 +200,6 @@ def validate(self):
self.validate_purpose()
self.validate_item()
self.validate_customer_provided_item()
- self.validate_qty()
self.set_transfer_qty()
self.validate_uom_is_integer("uom", "qty")
self.validate_uom_is_integer("stock_uom", "transfer_qty")
@@ -231,7 +230,7 @@ def validate(self):
self.validate_serialized_batch()
self.calculate_rate_and_amount()
self.validate_putaway_capacity()
- self.validate_component_quantities()
+ self.validate_component_and_quantities()
if not self.get("purpose") == "Manufacture":
# ignore scrap item wh difference and empty source/target wh
@@ -425,39 +424,6 @@ def validate_item(self):
flt(item.qty) * flt(item.conversion_factor), self.precision("transfer_qty", item)
)
- def validate_qty(self):
- manufacture_purpose = ["Manufacture", "Material Consumption for Manufacture"]
-
- if self.purpose in manufacture_purpose and self.work_order:
- if not frappe.get_value("Work Order", self.work_order, "skip_transfer"):
- item_code = []
- for item in self.items:
- if cstr(item.t_warehouse) == "":
- req_items = frappe.get_all(
- "Work Order Item",
- filters={"parent": self.work_order, "item_code": item.item_code},
- fields=["item_code"],
- )
-
- transferred_materials = frappe.db.sql(
- """
- select
- sum(sed.qty) as qty
- from `tabStock Entry` se,`tabStock Entry Detail` sed
- where
- se.name = sed.parent and se.docstatus=1 and
- (se.purpose='Material Transfer for Manufacture' or se.purpose='Manufacture')
- and sed.item_code=%s and se.work_order= %s and ifnull(sed.t_warehouse, '') != ''
- """,
- (item.item_code, self.work_order),
- as_dict=1,
- )
-
- stock_qty = flt(item.qty)
- trans_qty = flt(transferred_materials[0].qty)
- if req_items:
- if stock_qty > trans_qty:
- item_code.append(item.item_code)
def validate_fg_completed_qty(self):
item_wise_qty = {}
@@ -710,7 +676,7 @@ def set_actual_qty(self):
title=_("Insufficient Stock"),
)
- def validate_component_quantities(self):
+ def validate_component_and_quantities(self):
if self.purpose not in ["Manufacture", "Material Transfer for Manufacture"]:
return
@@ -723,20 +689,29 @@ def validate_component_quantities(self):
raw_materials = self.get_bom_raw_materials(self.fg_completed_qty)
precision = frappe.get_precision("Stock Entry Detail", "qty")
- for row in self.items:
- if not row.s_warehouse:
- continue
-
- if details := raw_materials.get(row.item_code):
- if flt(details.get("qty"), precision) != flt(row.qty, precision):
+ for item_code, details in raw_materials.items():
+ if matched_item := self.get_matched_items(item_code):
+ if flt(details.get("qty"), precision) != flt(matched_item.qty, precision):
frappe.throw(
_("For the item {0}, the quantity should be {1} according to the BOM {2}.").format(
- frappe.bold(row.item_code),
- flt(details.get("qty"), precision),
+ frappe.bold(item_code),
+ flt(details.get("qty")),
get_link_to_form("BOM", self.bom_no),
),
title=_("Incorrect Component Quantity"),
)
+ else:
+ frappe.throw(
+ _("According to the BOM {0}, the Item '{1}' is missing in the stock entry.").format(
+ get_link_to_form("BOM", self.bom_no), frappe.bold(item_code)
+ ),
+ title=_("Missing Item"),
+ )
+ def get_matched_items(self, item_code):
+ for row in self.items:
+ if row.item_code == item_code:
+ return row
+ return {}
@frappe.whitelist()
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 5705da9dd984..e04003f7a4f1 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -20,6 +20,7 @@
)
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.material_request.test_material_request import get_gle, make_material_request
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.serial_no.serial_no import *
from erpnext.stock.doctype.stock_entry.stock_entry import FinishedGoodError, make_stock_in_entry
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
@@ -1960,6 +1961,156 @@ def test_stock_entry_ledgers_for_mr_purpose_and_TC_SCK_052(self):
self.assertEqual(sh_gle[0], sh_gle[1])
self.assertEqual(sa_gle[0], sa_gle[1])
+ def test_create_stock_repack_via_bom_TC_SCK_016(self):
+ self.create_stock_repack_via_bom_TC_SCK_016()
+
+ def test_create_and_cancel_stock_repack_via_bom_TC_SCK_065(self):
+ se = self.create_stock_repack_via_bom_TC_SCK_016()
+ se.cancel()
+
+ sl_entry_cancelled = frappe.db.get_all(
+ "Stock Ledger Entry",
+ {"voucher_type": "Stock Entry", "voucher_no": se.name},
+ ["actual_qty", "warehouse"],
+ order_by="creation",
+ )
+ warehouse_qty = {
+ "_Test Target Warehouse - _TC": 0,
+ "_Test Warehouse - _TC": 0
+ }
+
+ for sle in sl_entry_cancelled:
+ warehouse_qty[sle.get('warehouse')] += sle.get('actual_qty')
+
+ self.assertEqual(len(sl_entry_cancelled), 4)
+ self.assertEqual(warehouse_qty["_Test Target Warehouse - _TC"], 0)
+ self.assertEqual(warehouse_qty["_Test Warehouse - _TC"], 0)
+
+ def create_stock_repack_via_bom_TC_SCK_016(self):
+ t_warehouse = create_warehouse(
+ warehouse_name="_Test Target Warehouse",
+ properties={"parent_warehouse": "All Warehouses - _TC"},
+ company="_Test Company",
+ )
+ fields = {
+ "is_stock_item": 1,
+ "stock_uom": "Kg",
+ "uoms": [
+ {
+ 'uom': "Kg",
+ "conversion_factor": 1
+ },
+ {
+ 'uom': "Tonne",
+ "conversion_factor": 1000
+ }
+ ]
+ }
+ if frappe.db.has_column("Item", "gst_hsn_code"):
+ fields["gst_hsn_code"] = "01011010"
+
+ item_wheet = make_item("_Test Item Wheet", properties=fields).name
+ fields["stock_uom"]= "Nos"
+ fields["uoms"]= [
+ {
+ 'uom': "Nos",
+ "conversion_factor": 1
+ },
+ {
+ 'uom': "Kg",
+ "conversion_factor": 10
+ }
+ ]
+ item_wheet_bag = make_item("_Test Item Wheet 10Kg Bag", properties=fields).name
+
+ # Create Purchase Receipt
+ pr = make_purchase_receipt(item_code=item_wheet, qty=1, rate=20000, uom="Tonne", stock_uom="Kg", conversion_factor=1000)
+
+ # Check Stock Ledger Entries
+ self.check_stock_ledger_entries(
+ "Purchase Receipt",
+ pr.name,
+ [
+ ["_Test Item Wheet", "_Test Warehouse - _TC", 1000],
+ ]
+ )
+
+ # Create BOM
+ rm_items=[{
+ "item_code": item_wheet,
+ "qty": 10,
+ "uom": "Kg"
+ }]
+ bom_doc = create_bom(
+ item_wheet_bag, rm_items
+ )
+
+ # Create Repack
+ se = make_stock_entry(
+ item_code=item_wheet,
+ expense_account="Stock Adjustment - _TC",
+ company = "_Test Company",
+ purpose="Repack",
+ qty=10,
+ do_not_submit=True,
+ do_not_save=True
+ )
+ se.from_bom = 1
+ se.bom_no = bom_doc.name
+ se.fg_completed_qty = 10
+ se.get_items()
+ se.items[0].s_warehouse = "_Test Warehouse - _TC"
+ se.items[0].t_warehouse = None
+ se.items[1].s_warehouse = None
+ se.items[1].t_warehouse = t_warehouse
+ se.save()
+ se.submit()
+
+ # Check Stock Ledger Entries
+ self.check_stock_ledger_entries(
+ "Stock Entry",
+ se.name,
+ [
+ ['_Test Item Wheet 10Kg Bag', '_Test Target Warehouse - _TC', 10.0],
+ ['_Test Item Wheet', '_Test Warehouse - _TC', -100.0],
+ ]
+ )
+
+ return se
+
+
+def create_bom(bom_item, rm_items, company=None, qty=None, properties=None):
+ bom = frappe.new_doc("BOM")
+ bom.update(
+ {
+ "item": bom_item or "_Test Item",
+ "company": company or "_Test Company",
+ "quantity": qty or 1,
+ }
+ )
+ if properties:
+ bom.update(properties)
+
+ for item in rm_items:
+ item_args = {}
+
+ item_args.update(
+ {
+ "item_code": item.get('item_code'),
+ "qty": item.get('qty'),
+ "uom": item.get('uom'),
+ "rate": item.get('rate')
+ }
+ )
+
+ bom.append("items", item_args)
+
+ bom.save(ignore_permissions=True)
+ bom.submit()
+
+ return bom
+
+
def make_serialized_item(**args):
args = frappe._dict(args)
se = frappe.copy_doc(test_records[0])
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index cf885c5f276b..622d2ce25702 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -1053,6 +1053,7 @@ def get_current_qty_for_batch_nos(self, doc):
posting_date=doc.posting_date,
posting_time=doc.posting_time,
ignore_voucher_nos=[doc.voucher_no],
+ for_stock_levels=True,
)
or 0
) * -1
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 3f04b06a75cf..bcd26cb473af 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -315,7 +315,6 @@ def prepare_stock_ledger_entries(self):
.where((sle.docstatus < 2) & (sle.is_cancelled == 0))
.orderby(sle.posting_datetime)
.orderby(sle.creation)
- .orderby(sle.actual_qty)
)
query = self.apply_inventory_dimensions_filters(query, sle)
diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json
index f3834c73a7ce..c8a374ecc5b3 100644
--- a/erpnext/support/web_form/issues/issues.json
+++ b/erpnext/support/web_form/issues/issues.json
@@ -54,7 +54,7 @@
"label": "Status",
"max_length": 0,
"max_value": 0,
- "options": "Open\nReplied\nHold\nClosed",
+ "options": "Open\nReplied\nOn Hold\nClosed",
"read_only": 1,
"reqd": 0,
"show_in_filter": 1