mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
Merge pull request #42541 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
@@ -73,7 +73,9 @@
|
|||||||
"remarks_section",
|
"remarks_section",
|
||||||
"general_ledger_remarks_length",
|
"general_ledger_remarks_length",
|
||||||
"column_break_lvjk",
|
"column_break_lvjk",
|
||||||
"receivable_payable_remarks_length"
|
"receivable_payable_remarks_length",
|
||||||
|
"payment_request_settings",
|
||||||
|
"create_pr_in_draft_status"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -475,6 +477,18 @@
|
|||||||
"fieldname": "calculate_depr_using_total_days",
|
"fieldname": "calculate_depr_using_total_days",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Calculate daily depreciation using total days in depreciation period"
|
"label": "Calculate daily depreciation using total days in depreciation period"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.",
|
||||||
|
"fieldname": "payment_request_settings",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Payment Request"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "1",
|
||||||
|
"fieldname": "create_pr_in_draft_status",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Create in Draft Status"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
@@ -482,7 +496,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-07-12 00:24:20.957726",
|
"modified": "2024-07-26 06:48:52.714630",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ class AccountsSettings(Document):
|
|||||||
book_tax_discount_loss: DF.Check
|
book_tax_discount_loss: DF.Check
|
||||||
calculate_depr_using_total_days: DF.Check
|
calculate_depr_using_total_days: DF.Check
|
||||||
check_supplier_invoice_uniqueness: DF.Check
|
check_supplier_invoice_uniqueness: DF.Check
|
||||||
|
create_pr_in_draft_status: DF.Check
|
||||||
credit_controller: DF.Link | None
|
credit_controller: DF.Link | None
|
||||||
delete_linked_ledger_entries: DF.Check
|
delete_linked_ledger_entries: DF.Check
|
||||||
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
|
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
|
||||||
|
|||||||
@@ -25,30 +25,6 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
erpnext.toggle_naming_series();
|
erpnext.toggle_naming_series();
|
||||||
|
|
||||||
if (frm.doc.repost_required && frm.doc.docstatus === 1) {
|
|
||||||
frm.set_intro(
|
|
||||||
__(
|
|
||||||
"Accounting entries for this Journal Entry need to be reposted. Please click on 'Repost' button to update."
|
|
||||||
)
|
|
||||||
);
|
|
||||||
frm.add_custom_button(__("Repost Accounting Entries"), () => {
|
|
||||||
frm.call({
|
|
||||||
doc: frm.doc,
|
|
||||||
method: "repost_accounting_entries",
|
|
||||||
freeze: true,
|
|
||||||
freeze_message: __("Reposting..."),
|
|
||||||
callback: (r) => {
|
|
||||||
if (!r.exc) {
|
|
||||||
frappe.msgprint(__("Accounting Entries are reposted."));
|
|
||||||
frm.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.removeClass("btn-default")
|
|
||||||
.addClass("btn-warning");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frm.doc.docstatus > 0) {
|
if (frm.doc.docstatus > 0) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Ledger"),
|
__("Ledger"),
|
||||||
|
|||||||
@@ -64,8 +64,7 @@
|
|||||||
"stock_entry",
|
"stock_entry",
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"auto_repeat",
|
"auto_repeat",
|
||||||
"amended_from",
|
"amended_from"
|
||||||
"repost_required"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -544,15 +543,6 @@
|
|||||||
"label": "Is System Generated",
|
"label": "Is System Generated",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "repost_required",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Repost Required",
|
|
||||||
"print_hide": 1,
|
|
||||||
"read_only": 1
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
@@ -567,7 +557,7 @@
|
|||||||
"table_fieldname": "payment_entries"
|
"table_fieldname": "payment_entries"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2023-11-23 12:11:04.128015",
|
"modified": "2024-07-18 15:32:29.413598",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Journal Entry",
|
"name": "Journal Entry",
|
||||||
|
|||||||
@@ -47,9 +47,7 @@ class JournalEntry(AccountsController):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import (
|
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import JournalEntryAccount
|
||||||
JournalEntryAccount,
|
|
||||||
)
|
|
||||||
|
|
||||||
accounts: DF.Table[JournalEntryAccount]
|
accounts: DF.Table[JournalEntryAccount]
|
||||||
amended_from: DF.Link | None
|
amended_from: DF.Link | None
|
||||||
@@ -197,14 +195,10 @@ class JournalEntry(AccountsController):
|
|||||||
self.update_booked_depreciation()
|
self.update_booked_depreciation()
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
if hasattr(self, "repost_required"):
|
self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []})
|
||||||
self.needs_repost = self.check_if_fields_updated(
|
if self.needs_repost:
|
||||||
fields_to_check=[], child_tables={"accounts": []}
|
self.validate_for_repost()
|
||||||
)
|
self.repost_accounting_entries()
|
||||||
if self.needs_repost:
|
|
||||||
self.validate_for_repost()
|
|
||||||
self.db_set("repost_required", self.needs_repost)
|
|
||||||
self.repost_accounting_entries()
|
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
# References for this Journal are removed on the `on_cancel` event in accounts_controller
|
# References for this Journal are removed on the `on_cancel` event in accounts_controller
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ frappe.ui.form.on("Payment Order", {
|
|||||||
|
|
||||||
// payment Entry
|
// payment Entry
|
||||||
if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") {
|
if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") {
|
||||||
frm.add_custom_button(__("Create Payment Entries"), function () {
|
frm.add_custom_button(__("Create Journal Entries"), function () {
|
||||||
frm.trigger("make_payment_records");
|
frm.trigger("make_payment_records");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -500,7 +500,8 @@ def make_payment_request(**args):
|
|||||||
if args.order_type == "Shopping Cart" or args.mute_email:
|
if args.order_type == "Shopping Cart" or args.mute_email:
|
||||||
pr.flags.mute_email = True
|
pr.flags.mute_email = True
|
||||||
|
|
||||||
pr.insert(ignore_permissions=True)
|
if frappe.db.get_single_value("Accounts Settings", "create_pr_in_draft_status", cache=True):
|
||||||
|
pr.insert(ignore_permissions=True)
|
||||||
if args.submit_doc:
|
if args.submit_doc:
|
||||||
pr.submit()
|
pr.submit()
|
||||||
|
|
||||||
|
|||||||
@@ -77,31 +77,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
|||||||
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
|
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.frm.doc.repost_required && this.frm.doc.docstatus === 1) {
|
|
||||||
this.frm.set_intro(
|
|
||||||
__(
|
|
||||||
"Accounting entries for this invoice need to be reposted. Please click on 'Repost' button to update."
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.frm
|
|
||||||
.add_custom_button(__("Repost Accounting Entries"), () => {
|
|
||||||
this.frm.call({
|
|
||||||
doc: this.frm.doc,
|
|
||||||
method: "repost_accounting_entries",
|
|
||||||
freeze: true,
|
|
||||||
freeze_message: __("Reposting..."),
|
|
||||||
callback: (r) => {
|
|
||||||
if (!r.exc) {
|
|
||||||
frappe.msgprint(__("Accounting Entries are reposted."));
|
|
||||||
me.frm.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.removeClass("btn-default")
|
|
||||||
.addClass("btn-warning");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0) {
|
if (!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0) {
|
||||||
if (doc.on_hold) {
|
if (doc.on_hold) {
|
||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
|
|||||||
@@ -170,7 +170,6 @@
|
|||||||
"against_expense_account",
|
"against_expense_account",
|
||||||
"column_break_63",
|
"column_break_63",
|
||||||
"unrealized_profit_loss_account",
|
"unrealized_profit_loss_account",
|
||||||
"repost_required",
|
|
||||||
"subscription_section",
|
"subscription_section",
|
||||||
"subscription",
|
"subscription",
|
||||||
"auto_repeat",
|
"auto_repeat",
|
||||||
@@ -364,7 +363,8 @@
|
|||||||
"description": "Once set, this invoice will be on hold till the set date",
|
"description": "Once set, this invoice will be on hold till the set date",
|
||||||
"fieldname": "release_date",
|
"fieldname": "release_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Release Date"
|
"label": "Release Date",
|
||||||
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "cb_17",
|
"fieldname": "cb_17",
|
||||||
@@ -1603,15 +1603,6 @@
|
|||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Use Company Default Round Off Cost Center"
|
"label": "Use Company Default Round Off Cost Center"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "repost_required",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Repost Required",
|
|
||||||
"options": "Account",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "use_transaction_date_exchange_rate",
|
"fieldname": "use_transaction_date_exchange_rate",
|
||||||
@@ -1639,7 +1630,7 @@
|
|||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-04-11 11:28:42.802211",
|
"modified": "2024-07-25 19:42:36.931278",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -159,7 +159,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
rejected_warehouse: DF.Link | None
|
rejected_warehouse: DF.Link | None
|
||||||
release_date: DF.Date | None
|
release_date: DF.Date | None
|
||||||
remarks: DF.SmallText | None
|
remarks: DF.SmallText | None
|
||||||
repost_required: DF.Check
|
|
||||||
represents_company: DF.Link | None
|
represents_company: DF.Link | None
|
||||||
return_against: DF.Link | None
|
return_against: DF.Link | None
|
||||||
rounded_total: DF.Currency
|
rounded_total: DF.Currency
|
||||||
@@ -796,19 +795,17 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.process_common_party_accounting()
|
self.process_common_party_accounting()
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
if hasattr(self, "repost_required"):
|
fields_to_check = [
|
||||||
fields_to_check = [
|
"cash_bank_account",
|
||||||
"cash_bank_account",
|
"write_off_account",
|
||||||
"write_off_account",
|
"unrealized_profit_loss_account",
|
||||||
"unrealized_profit_loss_account",
|
"is_opening",
|
||||||
"is_opening",
|
]
|
||||||
]
|
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
|
||||||
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
|
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
||||||
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
if self.needs_repost:
|
||||||
if self.needs_repost:
|
self.validate_for_repost()
|
||||||
self.validate_for_repost()
|
self.repost_accounting_entries()
|
||||||
self.db_set("repost_required", self.needs_repost)
|
|
||||||
self.repost_accounting_entries()
|
|
||||||
|
|
||||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||||
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
|
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
|
||||||
@@ -1705,6 +1702,9 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.db_set("release_date", None)
|
self.db_set("release_date", None)
|
||||||
|
|
||||||
def set_tax_withholding(self):
|
def set_tax_withholding(self):
|
||||||
|
self.set("advance_tax", [])
|
||||||
|
self.set("tax_withheld_vouchers", [])
|
||||||
|
|
||||||
if not self.apply_tds:
|
if not self.apply_tds:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1746,8 +1746,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.remove(d)
|
self.remove(d)
|
||||||
|
|
||||||
## Add pending vouchers on which tax was withheld
|
## Add pending vouchers on which tax was withheld
|
||||||
self.set("tax_withheld_vouchers", [])
|
|
||||||
|
|
||||||
for voucher_no, voucher_details in voucher_wise_amount.items():
|
for voucher_no, voucher_details in voucher_wise_amount.items():
|
||||||
self.append(
|
self.append(
|
||||||
"tax_withheld_vouchers",
|
"tax_withheld_vouchers",
|
||||||
@@ -1762,7 +1760,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.calculate_taxes_and_totals()
|
self.calculate_taxes_and_totals()
|
||||||
|
|
||||||
def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
|
def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
|
||||||
self.set("advance_tax", [])
|
|
||||||
for tax in advance_taxes:
|
for tax in advance_taxes:
|
||||||
allocated_amount = 0
|
allocated_amount = 0
|
||||||
pending_amount = flt(tax.tax_amount - tax.allocated_amount)
|
pending_amount = flt(tax.tax_amount - tax.allocated_amount)
|
||||||
|
|||||||
@@ -2014,8 +2014,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
|||||||
["Service - _TC", 1000, 0.0, nowdate()],
|
["Service - _TC", 1000, 0.0, nowdate()],
|
||||||
]
|
]
|
||||||
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
check_gl_entries(self, pi.name, expected_gle, nowdate())
|
||||||
pi.load_from_db()
|
|
||||||
self.assertFalse(pi.repost_required)
|
|
||||||
|
|
||||||
@change_settings("Buying Settings", {"supplier_group": None})
|
@change_settings("Buying Settings", {"supplier_group": None})
|
||||||
def test_purchase_invoice_without_supplier_group(self):
|
def test_purchase_invoice_without_supplier_group(self):
|
||||||
|
|||||||
@@ -68,31 +68,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
|
|
||||||
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
|
||||||
|
|
||||||
if (this.frm.doc.repost_required && this.frm.doc.docstatus === 1) {
|
|
||||||
this.frm.set_intro(
|
|
||||||
__(
|
|
||||||
"Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."
|
|
||||||
)
|
|
||||||
);
|
|
||||||
this.frm
|
|
||||||
.add_custom_button(__("Repost Accounting Entries"), () => {
|
|
||||||
this.frm.call({
|
|
||||||
doc: this.frm.doc,
|
|
||||||
method: "repost_accounting_entries",
|
|
||||||
freeze: true,
|
|
||||||
freeze_message: __("Reposting..."),
|
|
||||||
callback: (r) => {
|
|
||||||
if (!r.exc) {
|
|
||||||
frappe.msgprint(__("Accounting Entries are reposted"));
|
|
||||||
me.frm.refresh();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.removeClass("btn-default")
|
|
||||||
.addClass("btn-warning");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.frm.doc.is_return) {
|
if (this.frm.doc.is_return) {
|
||||||
this.frm.return_print_format = "Sales Invoice Return";
|
this.frm.return_print_format = "Sales Invoice Return";
|
||||||
}
|
}
|
||||||
@@ -502,11 +477,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
frappe.msgprint(__("Please specify Company to proceed"));
|
frappe.msgprint(__("Please specify Company to proceed"));
|
||||||
} else {
|
} else {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
const for_validate = me.frm.doc.is_return ? true : false;
|
||||||
return this.frm.call({
|
return this.frm.call({
|
||||||
doc: me.frm.doc,
|
doc: me.frm.doc,
|
||||||
method: "set_missing_values",
|
method: "set_missing_values",
|
||||||
args: {
|
args: {
|
||||||
for_validate: true,
|
for_validate: for_validate,
|
||||||
},
|
},
|
||||||
callback: function (r) {
|
callback: function (r) {
|
||||||
if (!r.exc) {
|
if (!r.exc) {
|
||||||
|
|||||||
@@ -213,7 +213,6 @@
|
|||||||
"is_internal_customer",
|
"is_internal_customer",
|
||||||
"is_discounted",
|
"is_discounted",
|
||||||
"remarks",
|
"remarks",
|
||||||
"repost_required",
|
|
||||||
"connections_tab"
|
"connections_tab"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@@ -2125,15 +2124,6 @@
|
|||||||
"label": "Write Off",
|
"label": "Write Off",
|
||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"fieldname": "repost_required",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"hidden": 1,
|
|
||||||
"label": "Repost Required",
|
|
||||||
"no_copy": 1,
|
|
||||||
"read_only": 1
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "incoterm",
|
"fieldname": "incoterm",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@@ -2188,7 +2178,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2024-06-07 16:49:32.458402",
|
"modified": "2024-07-18 15:30:39.428519",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ class SalesInvoice(SellingController):
|
|||||||
project: DF.Link | None
|
project: DF.Link | None
|
||||||
redeem_loyalty_points: DF.Check
|
redeem_loyalty_points: DF.Check
|
||||||
remarks: DF.SmallText | None
|
remarks: DF.SmallText | None
|
||||||
repost_required: DF.Check
|
|
||||||
represents_company: DF.Link | None
|
represents_company: DF.Link | None
|
||||||
return_against: DF.Link | None
|
return_against: DF.Link | None
|
||||||
rounded_total: DF.Currency
|
rounded_total: DF.Currency
|
||||||
@@ -556,7 +555,6 @@ class SalesInvoice(SellingController):
|
|||||||
self.repost_future_sle_and_gle()
|
self.repost_future_sle_and_gle()
|
||||||
|
|
||||||
self.db_set("status", "Cancelled")
|
self.db_set("status", "Cancelled")
|
||||||
self.db_set("repost_required", 0)
|
|
||||||
|
|
||||||
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
|
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
|
||||||
update_company_current_month_sales(self.company)
|
update_company_current_month_sales(self.company)
|
||||||
@@ -706,25 +704,23 @@ class SalesInvoice(SellingController):
|
|||||||
data.sales_invoice = sales_invoice
|
data.sales_invoice = sales_invoice
|
||||||
|
|
||||||
def on_update_after_submit(self):
|
def on_update_after_submit(self):
|
||||||
if hasattr(self, "repost_required"):
|
fields_to_check = [
|
||||||
fields_to_check = [
|
"additional_discount_account",
|
||||||
"additional_discount_account",
|
"cash_bank_account",
|
||||||
"cash_bank_account",
|
"account_for_change_amount",
|
||||||
"account_for_change_amount",
|
"write_off_account",
|
||||||
"write_off_account",
|
"loyalty_redemption_account",
|
||||||
"loyalty_redemption_account",
|
"unrealized_profit_loss_account",
|
||||||
"unrealized_profit_loss_account",
|
"is_opening",
|
||||||
"is_opening",
|
]
|
||||||
]
|
child_tables = {
|
||||||
child_tables = {
|
"items": ("income_account", "expense_account", "discount_account"),
|
||||||
"items": ("income_account", "expense_account", "discount_account"),
|
"taxes": ("account_head",),
|
||||||
"taxes": ("account_head",),
|
}
|
||||||
}
|
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
||||||
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
|
if self.needs_repost:
|
||||||
if self.needs_repost:
|
self.validate_for_repost()
|
||||||
self.validate_for_repost()
|
self.repost_accounting_entries()
|
||||||
self.db_set("repost_required", self.needs_repost)
|
|
||||||
self.repost_accounting_entries()
|
|
||||||
|
|
||||||
def set_paid_amount(self):
|
def set_paid_amount(self):
|
||||||
paid_amount = 0.0
|
paid_amount = 0.0
|
||||||
|
|||||||
@@ -2952,9 +2952,6 @@ class TestSalesInvoice(FrappeTestCase):
|
|||||||
|
|
||||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||||
|
|
||||||
si.load_from_db()
|
|
||||||
self.assertFalse(si.repost_required)
|
|
||||||
|
|
||||||
def test_asset_depreciation_on_sale_with_pro_rata(self):
|
def test_asset_depreciation_on_sale_with_pro_rata(self):
|
||||||
"""
|
"""
|
||||||
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
|
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
|
||||||
|
|||||||
@@ -268,6 +268,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
|||||||
vouchers, voucher_wise_amount = get_invoice_vouchers(
|
vouchers, voucher_wise_amount = get_invoice_vouchers(
|
||||||
parties, tax_details, inv.company, party_type=party_type
|
parties, tax_details, inv.company, party_type=party_type
|
||||||
)
|
)
|
||||||
|
|
||||||
|
payment_entry_vouchers = get_payment_entry_vouchers(
|
||||||
|
parties, tax_details, inv.company, party_type=party_type
|
||||||
|
)
|
||||||
|
|
||||||
advance_vouchers = get_advance_vouchers(
|
advance_vouchers = get_advance_vouchers(
|
||||||
parties,
|
parties,
|
||||||
company=inv.company,
|
company=inv.company,
|
||||||
@@ -275,7 +280,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
|
|||||||
to_date=tax_details.to_date,
|
to_date=tax_details.to_date,
|
||||||
party_type=party_type,
|
party_type=party_type,
|
||||||
)
|
)
|
||||||
taxable_vouchers = vouchers + advance_vouchers
|
|
||||||
|
taxable_vouchers = vouchers + advance_vouchers + payment_entry_vouchers
|
||||||
tax_deducted_on_advances = 0
|
tax_deducted_on_advances = 0
|
||||||
|
|
||||||
if inv.doctype == "Purchase Invoice":
|
if inv.doctype == "Purchase Invoice":
|
||||||
@@ -387,6 +393,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
|
|||||||
return vouchers, voucher_wise_amount
|
return vouchers, voucher_wise_amount
|
||||||
|
|
||||||
|
|
||||||
|
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
|
||||||
|
payment_entry_filters = {
|
||||||
|
"party_type": party_type,
|
||||||
|
"party": ("in", parties),
|
||||||
|
"docstatus": 1,
|
||||||
|
"apply_tax_withholding_amount": 1,
|
||||||
|
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
|
||||||
|
"tax_withholding_category": tax_details.get("tax_withholding_category"),
|
||||||
|
"company": company,
|
||||||
|
}
|
||||||
|
|
||||||
|
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
|
||||||
|
|
||||||
|
|
||||||
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
|
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
|
||||||
"""
|
"""
|
||||||
Use Payment Ledger to fetch unallocated Advance Payments
|
Use Payment Ledger to fetch unallocated Advance Payments
|
||||||
|
|||||||
@@ -139,6 +139,7 @@ class ReceivablePayableReport:
|
|||||||
paid_in_account_currency=0.0,
|
paid_in_account_currency=0.0,
|
||||||
credit_note_in_account_currency=0.0,
|
credit_note_in_account_currency=0.0,
|
||||||
outstanding_in_account_currency=0.0,
|
outstanding_in_account_currency=0.0,
|
||||||
|
cost_center=ple.cost_center,
|
||||||
)
|
)
|
||||||
self.get_invoices(ple)
|
self.get_invoices(ple)
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ class ReceivablePayableReport:
|
|||||||
row.paid -= amount
|
row.paid -= amount
|
||||||
row.paid_in_account_currency -= amount_in_account_currency
|
row.paid_in_account_currency -= amount_in_account_currency
|
||||||
|
|
||||||
if ple.cost_center:
|
if not row.cost_center and ple.cost_center:
|
||||||
row.cost_center = str(ple.cost_center)
|
row.cost_center = str(ple.cost_center)
|
||||||
|
|
||||||
def update_sub_total_row(self, row, party):
|
def update_sub_total_row(self, row, party):
|
||||||
|
|||||||
@@ -53,11 +53,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
|||||||
si = si.submit()
|
si = si.submit()
|
||||||
return si
|
return si
|
||||||
|
|
||||||
def create_payment_entry(self, docname):
|
def create_payment_entry(self, docname, do_not_submit=False):
|
||||||
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
|
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
|
||||||
pe.paid_from = self.debit_to
|
pe.paid_from = self.debit_to
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
if not do_not_submit:
|
||||||
|
pe.submit()
|
||||||
|
return pe
|
||||||
|
|
||||||
def create_credit_note(self, docname, do_not_submit=False):
|
def create_credit_note(self, docname, do_not_submit=False):
|
||||||
credit_note = create_sales_invoice(
|
credit_note = create_sales_invoice(
|
||||||
@@ -984,3 +986,40 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
|
|||||||
expected_data_after_payment,
|
expected_data_after_payment,
|
||||||
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
|
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_cost_center_on_report_output(self):
|
||||||
|
filters = {
|
||||||
|
"company": self.company,
|
||||||
|
"report_date": today(),
|
||||||
|
"range1": 30,
|
||||||
|
"range2": 60,
|
||||||
|
"range3": 90,
|
||||||
|
"range4": 120,
|
||||||
|
}
|
||||||
|
|
||||||
|
# check invoice grand total and invoiced column's value for 3 payment terms
|
||||||
|
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
|
||||||
|
si.cost_center = self.cost_center
|
||||||
|
si.save().submit()
|
||||||
|
|
||||||
|
new_cc = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Cost Center",
|
||||||
|
"cost_center_name": "East Wing",
|
||||||
|
"parent_cost_center": self.company + " - " + self.company_abbr,
|
||||||
|
"company": self.company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
new_cc.save()
|
||||||
|
|
||||||
|
# check invoice grand total, invoiced, paid and outstanding column's value after payment
|
||||||
|
pe = self.create_payment_entry(si.name, do_not_submit=True)
|
||||||
|
pe.cost_center = new_cc.name
|
||||||
|
pe.save().submit()
|
||||||
|
report = execute(filters)
|
||||||
|
|
||||||
|
expected_data_after_payment = [si.name, si.cost_center, 60]
|
||||||
|
|
||||||
|
self.assertEqual(len(report[1]), 1)
|
||||||
|
row = report[1][0]
|
||||||
|
self.assertEqual(expected_data_after_payment, [row.voucher_no, row.cost_center, row.outstanding])
|
||||||
|
|||||||
@@ -315,8 +315,9 @@ def apply_conditions(query, pi, pii, filters):
|
|||||||
|
|
||||||
|
|
||||||
def get_items(filters, additional_table_columns):
|
def get_items(filters, additional_table_columns):
|
||||||
pi = frappe.qb.DocType("Purchase Invoice")
|
doctype = "Purchase Invoice"
|
||||||
pii = frappe.qb.DocType("Purchase Invoice Item")
|
pi = frappe.qb.DocType(doctype)
|
||||||
|
pii = frappe.qb.DocType(f"{doctype} Item")
|
||||||
Item = frappe.qb.DocType("Item")
|
Item = frappe.qb.DocType("Item")
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(pi)
|
frappe.qb.from_(pi)
|
||||||
@@ -353,6 +354,7 @@ def get_items(filters, additional_table_columns):
|
|||||||
pi.mode_of_payment,
|
pi.mode_of_payment,
|
||||||
)
|
)
|
||||||
.where(pi.docstatus == 1)
|
.where(pi.docstatus == 1)
|
||||||
|
.where(pii.parenttype == doctype)
|
||||||
)
|
)
|
||||||
|
|
||||||
if filters.get("supplier"):
|
if filters.get("supplier"):
|
||||||
|
|||||||
@@ -410,8 +410,9 @@ def apply_group_by_conditions(query, si, ii, filters):
|
|||||||
|
|
||||||
|
|
||||||
def get_items(filters, additional_query_columns, additional_conditions=None):
|
def get_items(filters, additional_query_columns, additional_conditions=None):
|
||||||
si = frappe.qb.DocType("Sales Invoice")
|
doctype = "Sales Invoice"
|
||||||
sii = frappe.qb.DocType("Sales Invoice Item")
|
si = frappe.qb.DocType(doctype)
|
||||||
|
sii = frappe.qb.DocType(f"{doctype} Item")
|
||||||
item = frappe.qb.DocType("Item")
|
item = frappe.qb.DocType("Item")
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
@@ -459,6 +460,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
|
|||||||
sii.qty,
|
sii.qty,
|
||||||
)
|
)
|
||||||
.where(si.docstatus == 1)
|
.where(si.docstatus == 1)
|
||||||
|
.where(sii.parenttype == doctype)
|
||||||
)
|
)
|
||||||
|
|
||||||
if additional_query_columns:
|
if additional_query_columns:
|
||||||
|
|||||||
@@ -1598,6 +1598,18 @@ def auto_create_exchange_rate_revaluation_weekly() -> None:
|
|||||||
create_err_and_its_journals(companies)
|
create_err_and_its_journals(companies)
|
||||||
|
|
||||||
|
|
||||||
|
def auto_create_exchange_rate_revaluation_monthly() -> None:
|
||||||
|
"""
|
||||||
|
Executed by background job
|
||||||
|
"""
|
||||||
|
companies = frappe.db.get_all(
|
||||||
|
"Company",
|
||||||
|
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Montly"},
|
||||||
|
fields=["name", "submit_err_jv"],
|
||||||
|
)
|
||||||
|
create_err_and_its_journals(companies)
|
||||||
|
|
||||||
|
|
||||||
def get_payment_ledger_entries(gl_entries, cancel=0):
|
def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||||
ple_map = []
|
ple_map = []
|
||||||
if gl_entries:
|
if gl_entries:
|
||||||
|
|||||||
@@ -740,7 +740,7 @@ class TestDepreciationMethods(AssetSetup):
|
|||||||
available_for_use_date="2030-06-06",
|
available_for_use_date="2030-06-06",
|
||||||
is_existing_asset=1,
|
is_existing_asset=1,
|
||||||
opening_number_of_booked_depreciations=2,
|
opening_number_of_booked_depreciations=2,
|
||||||
opening_accumulated_depreciation=47095.89,
|
opening_accumulated_depreciation=47178.08,
|
||||||
expected_value_after_useful_life=10000,
|
expected_value_after_useful_life=10000,
|
||||||
depreciation_start_date="2032-12-31",
|
depreciation_start_date="2032-12-31",
|
||||||
total_number_of_depreciations=3,
|
total_number_of_depreciations=3,
|
||||||
@@ -748,7 +748,7 @@ class TestDepreciationMethods(AssetSetup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(asset.status, "Draft")
|
self.assertEqual(asset.status, "Draft")
|
||||||
expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
|
expected_schedules = [["2032-12-31", 30000.0, 77178.08], ["2033-06-06", 12821.92, 90000.0]]
|
||||||
schedules = [
|
schedules = [
|
||||||
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||||
for d in get_depr_schedule(asset.name, "Draft")
|
for d in get_depr_schedule(asset.name, "Draft")
|
||||||
|
|||||||
@@ -552,9 +552,18 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
|
|||||||
# if not existing asset, from_date = available_for_use_date
|
# if not existing asset, from_date = available_for_use_date
|
||||||
# otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
|
# otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
|
||||||
# from_date = 01/01/2022
|
# from_date = 01/01/2022
|
||||||
from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
|
if row.depreciation_method in ("Straight Line", "Manual"):
|
||||||
days = date_diff(row.depreciation_start_date, from_date) + 1
|
prev_depreciation_start_date = add_months(
|
||||||
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
|
row.depreciation_start_date,
|
||||||
|
(row.frequency_of_depreciation * -1) * asset_doc.opening_number_of_booked_depreciations,
|
||||||
|
)
|
||||||
|
from_date = asset_doc.available_for_use_date
|
||||||
|
days = date_diff(prev_depreciation_start_date, from_date) + 1
|
||||||
|
total_days = get_total_days(prev_depreciation_start_date, row.frequency_of_depreciation)
|
||||||
|
else:
|
||||||
|
from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
|
||||||
|
days = date_diff(row.depreciation_start_date, from_date) + 1
|
||||||
|
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
|
||||||
if days <= 0:
|
if days <= 0:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
@@ -682,20 +691,15 @@ def get_straight_line_or_manual_depr_amount(
|
|||||||
# if the Depreciation Schedule is being prepared for the first time
|
# if the Depreciation Schedule is being prepared for the first time
|
||||||
else:
|
else:
|
||||||
if row.daily_prorata_based:
|
if row.daily_prorata_based:
|
||||||
amount = (
|
amount = flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
|
||||||
flt(asset.gross_purchase_amount)
|
|
||||||
- flt(asset.opening_accumulated_depreciation)
|
|
||||||
- flt(row.expected_value_after_useful_life)
|
|
||||||
)
|
|
||||||
return get_daily_prorata_based_straight_line_depr(
|
return get_daily_prorata_based_straight_line_depr(
|
||||||
asset, row, schedule_idx, number_of_pending_depreciations, amount
|
asset, row, schedule_idx, number_of_pending_depreciations, amount
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return (
|
depreciation_amount = (
|
||||||
flt(asset.gross_purchase_amount)
|
flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
|
||||||
- flt(asset.opening_accumulated_depreciation)
|
) / flt(row.total_number_of_depreciations)
|
||||||
- flt(row.expected_value_after_useful_life)
|
return depreciation_amount
|
||||||
) / flt(row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations)
|
|
||||||
|
|
||||||
|
|
||||||
def get_daily_prorata_based_straight_line_depr(
|
def get_daily_prorata_based_straight_line_depr(
|
||||||
@@ -725,7 +729,11 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount):
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
add_days(
|
add_days(
|
||||||
get_last_day(add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)),
|
add_months(
|
||||||
|
row.depreciation_start_date,
|
||||||
|
(row.frequency_of_depreciation * (asset.opening_number_of_booked_depreciations + 1))
|
||||||
|
* -1,
|
||||||
|
),
|
||||||
1,
|
1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -904,7 +912,7 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount(
|
|||||||
|
|
||||||
|
|
||||||
def get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value):
|
def get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value):
|
||||||
""" "
|
"""
|
||||||
Returns monthly depreciation amount when year changes
|
Returns monthly depreciation amount when year changes
|
||||||
1. Calculate per day depr based on new year
|
1. Calculate per day depr based on new year
|
||||||
2. Calculate monthly amount based on new per day amount
|
2. Calculate monthly amount based on new per day amount
|
||||||
|
|||||||
@@ -75,6 +75,116 @@ class TestAssetDepreciationSchedule(FrappeTestCase):
|
|||||||
]
|
]
|
||||||
self.assertEqual(schedules, expected_schedules)
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
|
||||||
|
def test_schedule_for_slm_for_existing_asset_daily_pro_rata_enabled(self):
|
||||||
|
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1)
|
||||||
|
asset = create_asset(
|
||||||
|
calculate_depreciation=1,
|
||||||
|
depreciation_method="Straight Line",
|
||||||
|
available_for_use_date="2023-10-10",
|
||||||
|
is_existing_asset=1,
|
||||||
|
opening_number_of_booked_depreciations=9,
|
||||||
|
opening_accumulated_depreciation=265,
|
||||||
|
depreciation_start_date="2024-07-31",
|
||||||
|
total_number_of_depreciations=24,
|
||||||
|
frequency_of_depreciation=1,
|
||||||
|
gross_purchase_amount=731,
|
||||||
|
daily_prorata_based=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_schedules = [
|
||||||
|
["2024-07-31", 31.0, 296.0],
|
||||||
|
["2024-08-31", 31.0, 327.0],
|
||||||
|
["2024-09-30", 30.0, 357.0],
|
||||||
|
["2024-10-31", 31.0, 388.0],
|
||||||
|
["2024-11-30", 30.0, 418.0],
|
||||||
|
["2024-12-31", 31.0, 449.0],
|
||||||
|
["2025-01-31", 31.0, 480.0],
|
||||||
|
["2025-02-28", 28.0, 508.0],
|
||||||
|
["2025-03-31", 31.0, 539.0],
|
||||||
|
["2025-04-30", 30.0, 569.0],
|
||||||
|
["2025-05-31", 31.0, 600.0],
|
||||||
|
["2025-06-30", 30.0, 630.0],
|
||||||
|
["2025-07-31", 31.0, 661.0],
|
||||||
|
["2025-08-31", 31.0, 692.0],
|
||||||
|
["2025-09-30", 30.0, 722.0],
|
||||||
|
["2025-10-10", 9.0, 731.0],
|
||||||
|
]
|
||||||
|
schedules = [
|
||||||
|
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||||
|
for d in get_depr_schedule(asset.name, "Draft")
|
||||||
|
]
|
||||||
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 0)
|
||||||
|
|
||||||
|
def test_schedule_for_slm_for_existing_asset(self):
|
||||||
|
asset = create_asset(
|
||||||
|
calculate_depreciation=1,
|
||||||
|
depreciation_method="Straight Line",
|
||||||
|
available_for_use_date="2023-10-10",
|
||||||
|
is_existing_asset=1,
|
||||||
|
opening_number_of_booked_depreciations=9,
|
||||||
|
opening_accumulated_depreciation=265.30,
|
||||||
|
depreciation_start_date="2024-07-31",
|
||||||
|
total_number_of_depreciations=24,
|
||||||
|
frequency_of_depreciation=1,
|
||||||
|
gross_purchase_amount=731,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_schedules = [
|
||||||
|
["2024-07-31", 30.46, 295.76],
|
||||||
|
["2024-08-31", 30.46, 326.22],
|
||||||
|
["2024-09-30", 30.46, 356.68],
|
||||||
|
["2024-10-31", 30.46, 387.14],
|
||||||
|
["2024-11-30", 30.46, 417.6],
|
||||||
|
["2024-12-31", 30.46, 448.06],
|
||||||
|
["2025-01-31", 30.46, 478.52],
|
||||||
|
["2025-02-28", 30.46, 508.98],
|
||||||
|
["2025-03-31", 30.46, 539.44],
|
||||||
|
["2025-04-30", 30.46, 569.9],
|
||||||
|
["2025-05-31", 30.46, 600.36],
|
||||||
|
["2025-06-30", 30.46, 630.82],
|
||||||
|
["2025-07-31", 30.46, 661.28],
|
||||||
|
["2025-08-31", 30.46, 691.74],
|
||||||
|
["2025-09-30", 30.46, 722.2],
|
||||||
|
["2025-10-10", 8.8, 731.0],
|
||||||
|
]
|
||||||
|
schedules = [
|
||||||
|
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||||
|
for d in get_depr_schedule(asset.name, "Draft")
|
||||||
|
]
|
||||||
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
|
||||||
|
def test_schedule_sl_method_for_existing_asset_with_frequency_of_3_months(self):
|
||||||
|
asset = create_asset(
|
||||||
|
calculate_depreciation=1,
|
||||||
|
depreciation_method="Straight Line",
|
||||||
|
available_for_use_date="2023-11-01",
|
||||||
|
is_existing_asset=1,
|
||||||
|
opening_number_of_booked_depreciations=4,
|
||||||
|
opening_accumulated_depreciation=223.15,
|
||||||
|
depreciation_start_date="2024-12-31",
|
||||||
|
total_number_of_depreciations=12,
|
||||||
|
frequency_of_depreciation=3,
|
||||||
|
gross_purchase_amount=731,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_schedules = [
|
||||||
|
["2024-12-31", 60.92, 284.07],
|
||||||
|
["2025-03-31", 60.92, 344.99],
|
||||||
|
["2025-06-30", 60.92, 405.91],
|
||||||
|
["2025-09-30", 60.92, 466.83],
|
||||||
|
["2025-12-31", 60.92, 527.75],
|
||||||
|
["2026-03-31", 60.92, 588.67],
|
||||||
|
["2026-06-30", 60.92, 649.59],
|
||||||
|
["2026-09-30", 60.92, 710.51],
|
||||||
|
["2026-11-01", 20.49, 731.0],
|
||||||
|
]
|
||||||
|
schedules = [
|
||||||
|
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||||
|
for d in get_depr_schedule(asset.name, "Draft")
|
||||||
|
]
|
||||||
|
self.assertEqual(schedules, expected_schedules)
|
||||||
|
|
||||||
# Enable Checkbox to Calculate depreciation using total days in depreciation period
|
# Enable Checkbox to Calculate depreciation using total days in depreciation period
|
||||||
def test_daily_prorata_based_depr_after_enabling_configuration(self):
|
def test_daily_prorata_based_depr_after_enabling_configuration(self):
|
||||||
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1)
|
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1)
|
||||||
|
|||||||
@@ -39,16 +39,14 @@ def validate_filters(filters):
|
|||||||
def get_data(filters):
|
def get_data(filters):
|
||||||
po = frappe.qb.DocType("Purchase Order")
|
po = frappe.qb.DocType("Purchase Order")
|
||||||
po_item = frappe.qb.DocType("Purchase Order Item")
|
po_item = frappe.qb.DocType("Purchase Order Item")
|
||||||
pi = frappe.qb.DocType("Purchase Invoice")
|
|
||||||
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
pi_item = frappe.qb.DocType("Purchase Invoice Item")
|
||||||
|
|
||||||
query = (
|
query = (
|
||||||
frappe.qb.from_(po)
|
frappe.qb.from_(po)
|
||||||
.from_(po_item)
|
.inner_join(po_item)
|
||||||
|
.on(po_item.parent == po.name)
|
||||||
.left_join(pi_item)
|
.left_join(pi_item)
|
||||||
.on(pi_item.po_detail == po_item.name & pi_item.docstatus == 1)
|
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
|
||||||
.left_join(pi)
|
|
||||||
.on(pi.name == pi_item.parent & pi.docstatus == 1)
|
|
||||||
.select(
|
.select(
|
||||||
po.transaction_date.as_("date"),
|
po.transaction_date.as_("date"),
|
||||||
po_item.schedule_date.as_("required_date"),
|
po_item.schedule_date.as_("required_date"),
|
||||||
|
|||||||
@@ -2489,16 +2489,12 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def repost_accounting_entries(self):
|
def repost_accounting_entries(self):
|
||||||
if self.repost_required:
|
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
|
||||||
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
|
repost_ledger.company = self.company
|
||||||
repost_ledger.company = self.company
|
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
|
||||||
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
|
repost_ledger.flags.ignore_permissions = True
|
||||||
repost_ledger.flags.ignore_permissions = True
|
repost_ledger.insert()
|
||||||
repost_ledger.insert()
|
repost_ledger.submit()
|
||||||
repost_ledger.submit()
|
|
||||||
self.db_set("repost_required", 0)
|
|
||||||
else:
|
|
||||||
frappe.throw(_("No updates pending for reposting"))
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
|
|||||||
@@ -640,6 +640,12 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
|||||||
def update_terms(source_doc, target_doc, source_parent):
|
def update_terms(source_doc, target_doc, source_parent):
|
||||||
target_doc.payment_amount = -source_doc.payment_amount
|
target_doc.payment_amount = -source_doc.payment_amount
|
||||||
|
|
||||||
|
def item_condition(doc):
|
||||||
|
if return_against_rejected_qty:
|
||||||
|
return doc.rejected_qty
|
||||||
|
|
||||||
|
return doc.qty
|
||||||
|
|
||||||
doclist = get_mapped_doc(
|
doclist = get_mapped_doc(
|
||||||
doctype,
|
doctype,
|
||||||
source_name,
|
source_name,
|
||||||
@@ -654,6 +660,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
|
|||||||
"doctype": doctype + " Item",
|
"doctype": doctype + " Item",
|
||||||
"field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"},
|
"field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"},
|
||||||
"postprocess": update_item,
|
"postprocess": update_item,
|
||||||
|
"condition": item_condition,
|
||||||
},
|
},
|
||||||
"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
|
"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -456,6 +456,7 @@ scheduler_events = {
|
|||||||
],
|
],
|
||||||
"monthly_long": [
|
"monthly_long": [
|
||||||
"erpnext.accounts.deferred_revenue.process_deferred_accounting",
|
"erpnext.accounts.deferred_revenue.process_deferred_accounting",
|
||||||
|
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_monthly",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -365,6 +365,12 @@ def get_children(doctype=None, parent=None, **kwargs):
|
|||||||
return frappe.get_all("BOM Creator Item", fields=fields, filters=query_filters, order_by="idx")
|
return frappe.get_all("BOM Creator Item", fields=fields, filters=query_filters, order_by="idx")
|
||||||
|
|
||||||
|
|
||||||
|
def get_parent_row_no(doc, name):
|
||||||
|
for row in doc.items:
|
||||||
|
if row.name == name:
|
||||||
|
return row.idx
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_item(**kwargs):
|
def add_item(**kwargs):
|
||||||
if isinstance(kwargs, str):
|
if isinstance(kwargs, str):
|
||||||
@@ -375,6 +381,11 @@ def add_item(**kwargs):
|
|||||||
|
|
||||||
doc = frappe.get_doc("BOM Creator", kwargs.parent)
|
doc = frappe.get_doc("BOM Creator", kwargs.parent)
|
||||||
item_info = get_item_details(kwargs.item_code)
|
item_info = get_item_details(kwargs.item_code)
|
||||||
|
|
||||||
|
parent_row_no = ""
|
||||||
|
if kwargs.fg_reference_id and doc.name != kwargs.fg_reference_id:
|
||||||
|
parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id)
|
||||||
|
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
{
|
{
|
||||||
"uom": item_info.stock_uom,
|
"uom": item_info.stock_uom,
|
||||||
@@ -383,6 +394,9 @@ def add_item(**kwargs):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if parent_row_no:
|
||||||
|
kwargs.update({"parent_row_no": parent_row_no})
|
||||||
|
|
||||||
doc.append("items", kwargs)
|
doc.append("items", kwargs)
|
||||||
doc.save()
|
doc.save()
|
||||||
|
|
||||||
|
|||||||
@@ -1781,6 +1781,12 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
|
|||||||
apply_price_list(item, reset_plc_conversion) {
|
apply_price_list(item, reset_plc_conversion) {
|
||||||
// We need to reset plc_conversion_rate sometimes because the call to
|
// We need to reset plc_conversion_rate sometimes because the call to
|
||||||
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
|
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
|
||||||
|
|
||||||
|
|
||||||
|
if (this.frm.doc.doctype === "Material Request") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!reset_plc_conversion) {
|
if (!reset_plc_conversion) {
|
||||||
this.frm.set_value("plc_conversion_rate", "");
|
this.frm.set_value("plc_conversion_rate", "");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -754,7 +754,7 @@
|
|||||||
"fieldname": "auto_err_frequency",
|
"fieldname": "auto_err_frequency",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Frequency",
|
"label": "Frequency",
|
||||||
"options": "Daily\nWeekly"
|
"options": "Daily\nWeekly\nMonthly"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -808,7 +808,7 @@
|
|||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-06-21 17:46:25.567565",
|
"modified": "2024-07-24 18:17:56.413971",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Company(NestedSet):
|
|||||||
accumulated_depreciation_account: DF.Link | None
|
accumulated_depreciation_account: DF.Link | None
|
||||||
allow_account_creation_against_child_company: DF.Check
|
allow_account_creation_against_child_company: DF.Check
|
||||||
asset_received_but_not_billed: DF.Link | None
|
asset_received_but_not_billed: DF.Link | None
|
||||||
auto_err_frequency: DF.Literal["Daily", "Weekly"]
|
auto_err_frequency: DF.Literal["Daily", "Weekly", "Monthly"]
|
||||||
auto_exchange_rate_revaluation: DF.Check
|
auto_exchange_rate_revaluation: DF.Check
|
||||||
book_advance_payments_in_separate_party_account: DF.Check
|
book_advance_payments_in_separate_party_account: DF.Check
|
||||||
capital_work_in_progress_account: DF.Link | None
|
capital_work_in_progress_account: DF.Link | None
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ def make_taxes_and_charges_template(company_name, doctype, template):
|
|||||||
doc.flags.ignore_links = True
|
doc.flags.ignore_links = True
|
||||||
doc.flags.ignore_validate = True
|
doc.flags.ignore_validate = True
|
||||||
doc.flags.ignore_mandatory = True
|
doc.flags.ignore_mandatory = True
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ def make_item_tax_template(company_name, template):
|
|||||||
# Ingone validations to make doctypes faster
|
# Ingone validations to make doctypes faster
|
||||||
doc.flags.ignore_links = True
|
doc.flags.ignore_links = True
|
||||||
doc.flags.ignore_validate = True
|
doc.flags.ignore_validate = True
|
||||||
doc.insert(ignore_permissions=True)
|
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
@@ -233,7 +233,7 @@ def get_or_create_account(company_name, account):
|
|||||||
doc = frappe.get_doc(account)
|
doc = frappe.get_doc(account)
|
||||||
doc.flags.ignore_links = True
|
doc.flags.ignore_links = True
|
||||||
doc.flags.ignore_validate = True
|
doc.flags.ignore_validate = True
|
||||||
doc.insert(ignore_permissions=True, ignore_mandatory=True)
|
doc.insert(ignore_permissions=True, ignore_mandatory=True, ignore_if_duplicate=True)
|
||||||
return doc
|
return doc
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import json
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests.utils import FrappeTestCase
|
from frappe.tests.utils import FrappeTestCase
|
||||||
from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
|
from frappe.utils import add_days, cstr, flt, getdate, nowdate, nowtime, today
|
||||||
|
|
||||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||||
from erpnext.accounts.utils import get_balance_on
|
from erpnext.accounts.utils import get_balance_on
|
||||||
@@ -2005,6 +2005,40 @@ class TestDeliveryNote(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, dn5.submit)
|
self.assertRaises(frappe.ValidationError, dn5.submit)
|
||||||
|
|
||||||
|
def test_warranty_expiry_date_for_serial_item(self):
|
||||||
|
item_code = make_item(
|
||||||
|
"Test Warranty Expiry Date Item",
|
||||||
|
properties={
|
||||||
|
"has_serial_no": 1,
|
||||||
|
"serial_no_series": "TWE.#####",
|
||||||
|
"is_stock_item": 1,
|
||||||
|
"warranty_period": 100,
|
||||||
|
},
|
||||||
|
).name
|
||||||
|
|
||||||
|
se = make_stock_entry(
|
||||||
|
item_code=item_code,
|
||||||
|
target="_Test Warehouse - _TC",
|
||||||
|
qty=2,
|
||||||
|
basic_rate=50,
|
||||||
|
posting_date=nowdate(),
|
||||||
|
)
|
||||||
|
|
||||||
|
serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||||
|
create_delivery_note(
|
||||||
|
item_code=item_code,
|
||||||
|
qty=2,
|
||||||
|
rate=300,
|
||||||
|
use_serial_batch_fields=0,
|
||||||
|
serial_no=serial_nos,
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in serial_nos:
|
||||||
|
sn = frappe.get_doc("Serial No", row)
|
||||||
|
self.assertEqual(getdate(sn.warranty_expiry_date), getdate(add_days(nowdate(), 100)))
|
||||||
|
self.assertEqual(sn.status, "Delivered")
|
||||||
|
self.assertEqual(sn.warranty_period, 100)
|
||||||
|
|
||||||
|
|
||||||
def create_delivery_note(**args):
|
def create_delivery_note(**args):
|
||||||
dn = frappe.new_doc("Delivery Note")
|
dn = frappe.new_doc("Delivery Note")
|
||||||
|
|||||||
@@ -1886,9 +1886,19 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
rate=100,
|
rate=100,
|
||||||
rejected_qty=2,
|
rejected_qty=2,
|
||||||
rejected_warehouse=rejected_warehouse,
|
rejected_warehouse=rejected_warehouse,
|
||||||
|
do_not_save=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
pr.append(
|
||||||
|
"items",
|
||||||
|
{"item_code": item_code, "qty": 2, "rate": 100, "warehouse": warehouse, "rejected_qty": 0},
|
||||||
|
)
|
||||||
|
pr.save()
|
||||||
|
pr.submit()
|
||||||
|
self.assertEqual(len(pr.items), 2)
|
||||||
|
|
||||||
pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
|
pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
|
||||||
|
self.assertEqual(len(pr_return.items), 1)
|
||||||
self.assertEqual(pr_return.items[0].warehouse, rejected_warehouse)
|
self.assertEqual(pr_return.items[0].warehouse, rejected_warehouse)
|
||||||
self.assertEqual(pr_return.items[0].qty, 2.0 * -1)
|
self.assertEqual(pr_return.items[0].qty, 2.0 * -1)
|
||||||
self.assertEqual(pr_return.items[0].rejected_qty, 0.0)
|
self.assertEqual(pr_return.items[0].rejected_qty, 0.0)
|
||||||
|
|||||||
@@ -92,8 +92,10 @@ class SerialandBatchBundle(Document):
|
|||||||
if self.type_of_transaction == "Maintenance":
|
if self.type_of_transaction == "Maintenance":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.validate_serial_nos_duplicate()
|
if not self.flags.ignore_validate_serial_batch or frappe.flags.in_test:
|
||||||
self.check_future_entries_exists()
|
self.validate_serial_nos_duplicate()
|
||||||
|
self.check_future_entries_exists()
|
||||||
|
|
||||||
self.set_is_outward()
|
self.set_is_outward()
|
||||||
self.calculate_total_qty()
|
self.calculate_total_qty()
|
||||||
self.set_warehouse()
|
self.set_warehouse()
|
||||||
@@ -340,6 +342,9 @@ class SerialandBatchBundle(Document):
|
|||||||
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
|
||||||
|
|
||||||
for d in self.entries:
|
for d in self.entries:
|
||||||
|
if (d.incoming_rate == rate) and d.qty and d.stock_value_difference:
|
||||||
|
continue
|
||||||
|
|
||||||
d.incoming_rate = flt(rate, precision)
|
d.incoming_rate = flt(rate, precision)
|
||||||
if d.qty:
|
if d.qty:
|
||||||
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)
|
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)
|
||||||
@@ -393,32 +398,6 @@ class SerialandBatchBundle(Document):
|
|||||||
|
|
||||||
self.calculate_qty_and_amount(save=True)
|
self.calculate_qty_and_amount(save=True)
|
||||||
self.validate_quantity(row, qty_field=qty_field)
|
self.validate_quantity(row, qty_field=qty_field)
|
||||||
self.set_warranty_expiry_date()
|
|
||||||
|
|
||||||
def set_warranty_expiry_date(self):
|
|
||||||
if self.type_of_transaction != "Outward":
|
|
||||||
return
|
|
||||||
|
|
||||||
if not (self.docstatus == 1 and self.voucher_type == "Delivery Note" and self.has_serial_no):
|
|
||||||
return
|
|
||||||
|
|
||||||
warranty_period = frappe.get_cached_value("Item", self.item_code, "warranty_period")
|
|
||||||
|
|
||||||
if not warranty_period:
|
|
||||||
return
|
|
||||||
|
|
||||||
warranty_expiry_date = add_days(self.posting_date, cint(warranty_period))
|
|
||||||
|
|
||||||
serial_nos = self.get_serial_nos()
|
|
||||||
if not serial_nos:
|
|
||||||
return
|
|
||||||
|
|
||||||
sn_table = frappe.qb.DocType("Serial No")
|
|
||||||
(
|
|
||||||
frappe.qb.update(sn_table)
|
|
||||||
.set(sn_table.warranty_expiry_date, warranty_expiry_date)
|
|
||||||
.where(sn_table.name.isin(serial_nos))
|
|
||||||
).run()
|
|
||||||
|
|
||||||
def validate_voucher_no(self):
|
def validate_voucher_no(self):
|
||||||
if not (self.voucher_type and self.voucher_no):
|
if not (self.voucher_type and self.voucher_no):
|
||||||
@@ -867,6 +846,9 @@ class SerialandBatchBundle(Document):
|
|||||||
self.validate_serial_nos_inventory()
|
self.validate_serial_nos_inventory()
|
||||||
|
|
||||||
def set_purchase_document_no(self):
|
def set_purchase_document_no(self):
|
||||||
|
if self.flags.ignore_validate_serial_batch:
|
||||||
|
return
|
||||||
|
|
||||||
if not self.has_serial_no:
|
if not self.has_serial_no:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -83,7 +83,8 @@
|
|||||||
"job_card",
|
"job_card",
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"credit_note",
|
"credit_note",
|
||||||
"is_return"
|
"is_return",
|
||||||
|
"tab_connections"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -683,6 +684,12 @@
|
|||||||
"label": "Asset Repair",
|
"label": "Asset Repair",
|
||||||
"options": "Asset Repair",
|
"options": "Asset Repair",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_connections",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Connections",
|
||||||
|
"show_dashboard": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
|
|||||||
26
erpnext/stock/doctype/stock_entry/stock_entry_dashboard.py
Normal file
26
erpnext/stock/doctype/stock_entry/stock_entry_dashboard.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from frappe import _
|
||||||
|
|
||||||
|
|
||||||
|
# Todo: non_standard_fieldnames is to be decided
|
||||||
|
def get_data():
|
||||||
|
return {
|
||||||
|
"fieldname": "stock_entry",
|
||||||
|
"non_standard_fieldnames": {
|
||||||
|
# "DocType Name": "Reference field name",
|
||||||
|
},
|
||||||
|
"internal_links": {
|
||||||
|
"Purchase Order": ["items", "purchase_order"],
|
||||||
|
"Subcontracting Order": ["items", "subcontracting_order"],
|
||||||
|
"Subcontracting Receipt": ["items", "subcontracting_receipt"],
|
||||||
|
},
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"label": _("Reference"),
|
||||||
|
"items": [
|
||||||
|
"Purchase Order",
|
||||||
|
"Subcontracting Order",
|
||||||
|
"Subcontracting Receipt",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -434,7 +434,6 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
"depends_on": "use_serial_batch_fields",
|
|
||||||
"description": "If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial \n / Batch Bundle. ",
|
"description": "If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial \n / Batch Bundle. ",
|
||||||
"fieldname": "do_not_update_serial_batch_on_creation_of_auto_bundle",
|
"fieldname": "do_not_update_serial_batch_on_creation_of_auto_bundle",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
@@ -460,7 +459,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-07-15 17:18:23.872161",
|
"modified": "2024-07-29 14:55:19.093508",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Stock Settings",
|
"name": "Stock Settings",
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
|
|
||||||
frappe.query_reports["Product Bundle Balance"] = {
|
frappe.query_reports["Product Bundle Balance"] = {
|
||||||
filters: [
|
filters: [
|
||||||
|
{
|
||||||
|
fieldname: "company",
|
||||||
|
label: __("Company"),
|
||||||
|
fieldtype: "Link",
|
||||||
|
options: "Company",
|
||||||
|
default: frappe.defaults.get_user_default("Company"),
|
||||||
|
reqd: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "date",
|
fieldname: "date",
|
||||||
label: __("Date"),
|
label: __("Date"),
|
||||||
|
|||||||
@@ -224,6 +224,9 @@ def get_stock_ledger_entries(filters, items):
|
|||||||
.where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items)))
|
.where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items)))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if filters.get("company"):
|
||||||
|
query = query.where(sle.company == filters.get("company"))
|
||||||
|
|
||||||
if date := filters.get("date"):
|
if date := filters.get("date"):
|
||||||
query = query.where(sle.posting_date <= date)
|
query = query.where(sle.posting_date <= date)
|
||||||
else:
|
else:
|
||||||
@@ -237,7 +240,7 @@ def get_stock_ledger_entries(filters, items):
|
|||||||
if warehouse_details:
|
if warehouse_details:
|
||||||
wh = frappe.qb.DocType("Warehouse")
|
wh = frappe.qb.DocType("Warehouse")
|
||||||
query = query.where(
|
query = query.where(
|
||||||
ExistsCriterion(
|
sle.warehouse.isin(
|
||||||
frappe.qb.from_(wh)
|
frappe.qb.from_(wh)
|
||||||
.select(wh.name)
|
.select(wh.name)
|
||||||
.where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt))
|
.where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import frappe
|
|||||||
from frappe import _, bold
|
from frappe import _, bold
|
||||||
from frappe.model.naming import make_autoname
|
from frappe.model.naming import make_autoname
|
||||||
from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp
|
from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp
|
||||||
from frappe.utils import cint, cstr, flt, get_link_to_form, now, nowtime, today
|
from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, now, nowtime, today
|
||||||
from pypika import Order
|
from pypika import Order
|
||||||
|
|
||||||
from erpnext.stock.deprecated_serial_batch import (
|
from erpnext.stock.deprecated_serial_batch import (
|
||||||
@@ -110,6 +110,7 @@ class SerialBatchBundle:
|
|||||||
"type_of_transaction": "Inward" if self.sle.actual_qty > 0 else "Outward",
|
"type_of_transaction": "Inward" if self.sle.actual_qty > 0 else "Outward",
|
||||||
"company": self.company,
|
"company": self.company,
|
||||||
"is_rejected": self.is_rejected_entry(),
|
"is_rejected": self.is_rejected_entry(),
|
||||||
|
"make_bundle_from_sle": 1,
|
||||||
}
|
}
|
||||||
).make_serial_and_batch_bundle()
|
).make_serial_and_batch_bundle()
|
||||||
|
|
||||||
@@ -160,12 +161,13 @@ class SerialBatchBundle:
|
|||||||
|
|
||||||
if msg:
|
if msg:
|
||||||
error_msg = (
|
error_msg = (
|
||||||
f"Serial and Batch Bundle not set for item {self.item_code} in warehouse {self.warehouse}."
|
f"Serial and Batch Bundle not set for item {self.item_code} in warehouse {self.warehouse}"
|
||||||
+ msg
|
+ msg
|
||||||
)
|
)
|
||||||
frappe.throw(_(error_msg))
|
frappe.throw(_(error_msg))
|
||||||
|
|
||||||
def set_serial_and_batch_bundle(self, sn_doc):
|
def set_serial_and_batch_bundle(self, sn_doc):
|
||||||
|
self.sle.auto_created_serial_and_batch_bundle = 1
|
||||||
self.sle.db_set({"serial_and_batch_bundle": sn_doc.name, "auto_created_serial_and_batch_bundle": 1})
|
self.sle.db_set({"serial_and_batch_bundle": sn_doc.name, "auto_created_serial_and_batch_bundle": 1})
|
||||||
|
|
||||||
if sn_doc.is_rejected:
|
if sn_doc.is_rejected:
|
||||||
@@ -324,6 +326,9 @@ class SerialBatchBundle:
|
|||||||
def set_warehouse_and_status_in_serial_nos(self):
|
def set_warehouse_and_status_in_serial_nos(self):
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
|
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
|
||||||
|
|
||||||
|
if self.sle.auto_created_serial_and_batch_bundle and self.sle.actual_qty > 0:
|
||||||
|
return
|
||||||
|
|
||||||
serial_nos = get_serial_nos(self.sle.serial_and_batch_bundle)
|
serial_nos = get_serial_nos(self.sle.serial_and_batch_bundle)
|
||||||
if not self.sle.serial_and_batch_bundle and self.sle.serial_no:
|
if not self.sle.serial_and_batch_bundle and self.sle.serial_no:
|
||||||
serial_nos = get_parsed_serial_nos(self.sle.serial_no)
|
serial_nos = get_parsed_serial_nos(self.sle.serial_no)
|
||||||
@@ -338,7 +343,8 @@ class SerialBatchBundle:
|
|||||||
status = "Delivered"
|
status = "Delivered"
|
||||||
|
|
||||||
sn_table = frappe.qb.DocType("Serial No")
|
sn_table = frappe.qb.DocType("Serial No")
|
||||||
(
|
|
||||||
|
query = (
|
||||||
frappe.qb.update(sn_table)
|
frappe.qb.update(sn_table)
|
||||||
.set(sn_table.warehouse, warehouse)
|
.set(sn_table.warehouse, warehouse)
|
||||||
.set(
|
.set(
|
||||||
@@ -351,7 +357,19 @@ class SerialBatchBundle:
|
|||||||
)
|
)
|
||||||
.set(sn_table.company, self.sle.company)
|
.set(sn_table.company, self.sle.company)
|
||||||
.where(sn_table.name.isin(serial_nos))
|
.where(sn_table.name.isin(serial_nos))
|
||||||
).run()
|
)
|
||||||
|
|
||||||
|
if status == "Delivered":
|
||||||
|
warranty_period = frappe.get_cached_value("Item", self.sle.item_code, "warranty_period")
|
||||||
|
if warranty_period:
|
||||||
|
warranty_expiry_date = add_days(self.sle.posting_date, cint(warranty_period))
|
||||||
|
query = query.set(sn_table.warranty_expiry_date, warranty_expiry_date)
|
||||||
|
query = query.set(sn_table.warranty_period, warranty_period)
|
||||||
|
else:
|
||||||
|
query = query.set(sn_table.warranty_expiry_date, None)
|
||||||
|
query = query.set(sn_table.warranty_period, 0)
|
||||||
|
|
||||||
|
query.run()
|
||||||
|
|
||||||
def set_batch_no_in_serial_nos(self):
|
def set_batch_no_in_serial_nos(self):
|
||||||
entries = frappe.get_all(
|
entries = frappe.get_all(
|
||||||
@@ -915,6 +933,10 @@ class SerialBatchCreation:
|
|||||||
if doc.voucher_no and frappe.get_cached_value(doc.voucher_type, doc.voucher_no, "docstatus") == 2:
|
if doc.voucher_no and frappe.get_cached_value(doc.voucher_type, doc.voucher_no, "docstatus") == 2:
|
||||||
doc.voucher_no = ""
|
doc.voucher_no = ""
|
||||||
|
|
||||||
|
doc.flags.ignore_validate_serial_batch = False
|
||||||
|
if self.get("make_bundle_from_sle") and self.type_of_transaction == "Inward":
|
||||||
|
doc.flags.ignore_validate_serial_batch = True
|
||||||
|
|
||||||
doc.save()
|
doc.save()
|
||||||
self.validate_qty(doc)
|
self.validate_qty(doc)
|
||||||
|
|
||||||
@@ -1107,6 +1129,10 @@ class SerialBatchCreation:
|
|||||||
msg = f"Please set Serial No Series in the item {self.item_code} or create Serial and Batch Bundle manually."
|
msg = f"Please set Serial No Series in the item {self.item_code} or create Serial and Batch Bundle manually."
|
||||||
frappe.throw(_(msg))
|
frappe.throw(_(msg))
|
||||||
|
|
||||||
|
voucher_no = ""
|
||||||
|
if self.get("voucher_no"):
|
||||||
|
voucher_no = self.get("voucher_no")
|
||||||
|
|
||||||
for _i in range(abs(cint(self.actual_qty))):
|
for _i in range(abs(cint(self.actual_qty))):
|
||||||
serial_no = make_autoname(self.serial_no_series, "Serial No")
|
serial_no = make_autoname(self.serial_no_series, "Serial No")
|
||||||
sr_nos.append(serial_no)
|
sr_nos.append(serial_no)
|
||||||
@@ -1124,6 +1150,7 @@ class SerialBatchCreation:
|
|||||||
self.item_name,
|
self.item_name,
|
||||||
self.description,
|
self.description,
|
||||||
"Active",
|
"Active",
|
||||||
|
voucher_no,
|
||||||
self.batch_no,
|
self.batch_no,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1142,6 +1169,7 @@ class SerialBatchCreation:
|
|||||||
"item_name",
|
"item_name",
|
||||||
"description",
|
"description",
|
||||||
"status",
|
"status",
|
||||||
|
"purchase_document_no",
|
||||||
"batch_no",
|
"batch_no",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -275,7 +275,9 @@ def repost_future_sle(
|
|||||||
)
|
)
|
||||||
affected_transactions.update(obj.affected_transactions)
|
affected_transactions.update(obj.affected_transactions)
|
||||||
|
|
||||||
distinct_item_warehouses[(args[i].get("item_code"), args[i].get("warehouse"))].reposting_status = True
|
key = (args[i].get("item_code"), args[i].get("warehouse"))
|
||||||
|
if distinct_item_warehouses.get(key):
|
||||||
|
distinct_item_warehouses[key].reposting_status = True
|
||||||
|
|
||||||
if obj.new_items_found:
|
if obj.new_items_found:
|
||||||
for _item_wh, data in distinct_item_warehouses.items():
|
for _item_wh, data in distinct_item_warehouses.items():
|
||||||
@@ -1588,9 +1590,11 @@ def get_stock_ledger_entries(
|
|||||||
if not previous_sle.get("posting_date"):
|
if not previous_sle.get("posting_date"):
|
||||||
previous_sle["posting_datetime"] = "1900-01-01 00:00:00"
|
previous_sle["posting_datetime"] = "1900-01-01 00:00:00"
|
||||||
else:
|
else:
|
||||||
previous_sle["posting_datetime"] = get_combine_datetime(
|
posting_time = previous_sle.get("posting_time")
|
||||||
previous_sle["posting_date"], previous_sle["posting_time"]
|
if not posting_time:
|
||||||
)
|
posting_time = "00:00:00"
|
||||||
|
|
||||||
|
previous_sle["posting_datetime"] = get_combine_datetime(previous_sle["posting_date"], posting_time)
|
||||||
|
|
||||||
if operator in (">", "<=") and previous_sle.get("name"):
|
if operator in (">", "<=") and previous_sle.get("name"):
|
||||||
conditions += " and name!=%(name)s"
|
conditions += " and name!=%(name)s"
|
||||||
|
|||||||
@@ -23,18 +23,6 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"project",
|
"project",
|
||||||
"address_and_contact_section",
|
|
||||||
"supplier_address",
|
|
||||||
"address_display",
|
|
||||||
"contact_person",
|
|
||||||
"contact_display",
|
|
||||||
"contact_mobile",
|
|
||||||
"contact_email",
|
|
||||||
"column_break_19",
|
|
||||||
"shipping_address",
|
|
||||||
"shipping_address_display",
|
|
||||||
"billing_address",
|
|
||||||
"billing_address_display",
|
|
||||||
"section_break_24",
|
"section_break_24",
|
||||||
"column_break_25",
|
"column_break_25",
|
||||||
"set_warehouse",
|
"set_warehouse",
|
||||||
@@ -48,10 +36,23 @@
|
|||||||
"raw_materials_supplied_section",
|
"raw_materials_supplied_section",
|
||||||
"set_reserve_warehouse",
|
"set_reserve_warehouse",
|
||||||
"supplied_items",
|
"supplied_items",
|
||||||
"additional_costs_section",
|
"tab_address_and_contact",
|
||||||
|
"supplier_address",
|
||||||
|
"address_display",
|
||||||
|
"contact_person",
|
||||||
|
"contact_display",
|
||||||
|
"contact_mobile",
|
||||||
|
"contact_email",
|
||||||
|
"column_break_19",
|
||||||
|
"shipping_address",
|
||||||
|
"shipping_address_display",
|
||||||
|
"billing_address",
|
||||||
|
"billing_address_display",
|
||||||
|
"tab_additional_costs",
|
||||||
"distribute_additional_costs_based_on",
|
"distribute_additional_costs_based_on",
|
||||||
"additional_costs",
|
"additional_costs",
|
||||||
"total_additional_costs",
|
"total_additional_costs",
|
||||||
|
"tab_other_info",
|
||||||
"order_status_section",
|
"order_status_section",
|
||||||
"status",
|
"status",
|
||||||
"column_break_39",
|
"column_break_39",
|
||||||
@@ -59,7 +60,8 @@
|
|||||||
"printing_settings_section",
|
"printing_settings_section",
|
||||||
"select_print_heading",
|
"select_print_heading",
|
||||||
"column_break_43",
|
"column_break_43",
|
||||||
"letter_head"
|
"letter_head",
|
||||||
|
"tab_connections"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -95,7 +97,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Supplier",
|
"label": "Job Worker",
|
||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
@@ -107,7 +109,7 @@
|
|||||||
"fieldname": "supplier_name",
|
"fieldname": "supplier_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"label": "Supplier Name",
|
"label": "Job Worker Name",
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -115,7 +117,7 @@
|
|||||||
"depends_on": "supplier",
|
"depends_on": "supplier",
|
||||||
"fieldname": "supplier_warehouse",
|
"fieldname": "supplier_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Warehouse",
|
"label": "Job Worker Warehouse",
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
@@ -166,9 +168,8 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"fieldname": "tab_address_and_contact",
|
||||||
"fieldname": "address_and_contact_section",
|
"fieldtype": "Tab Break",
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Address and Contact"
|
"label": "Address and Contact"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -176,14 +177,14 @@
|
|||||||
"fetch_if_empty": 1,
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "supplier_address",
|
"fieldname": "supplier_address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Address",
|
"label": "Job Worker Address",
|
||||||
"options": "Address",
|
"options": "Address",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "address_display",
|
"fieldname": "address_display",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Supplier Address Details",
|
"label": "Job Worker Address Details",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -191,7 +192,7 @@
|
|||||||
"fetch_if_empty": 1,
|
"fetch_if_empty": 1,
|
||||||
"fieldname": "contact_person",
|
"fieldname": "contact_person",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Contact",
|
"label": "Job Worker Contact",
|
||||||
"options": "Contact",
|
"options": "Contact",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
@@ -337,11 +338,9 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
|
||||||
"collapsible_depends_on": "total_additional_costs",
|
|
||||||
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
|
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
|
||||||
"fieldname": "additional_costs_section",
|
"fieldname": "tab_additional_costs",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Tab Break",
|
||||||
"label": "Additional Costs"
|
"label": "Additional Costs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -449,6 +448,17 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Project",
|
"label": "Project",
|
||||||
"options": "Project"
|
"options": "Project"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_other_info",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Other Info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_connections",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Connections",
|
||||||
|
"show_dashboard": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-file-text",
|
"icon": "fa fa-file-text",
|
||||||
|
|||||||
@@ -23,18 +23,6 @@
|
|||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
"project",
|
"project",
|
||||||
"section_addresses",
|
|
||||||
"supplier_address",
|
|
||||||
"contact_person",
|
|
||||||
"address_display",
|
|
||||||
"contact_display",
|
|
||||||
"contact_mobile",
|
|
||||||
"contact_email",
|
|
||||||
"col_break_address",
|
|
||||||
"shipping_address",
|
|
||||||
"shipping_address_display",
|
|
||||||
"billing_address",
|
|
||||||
"billing_address_display",
|
|
||||||
"sec_warehouse",
|
"sec_warehouse",
|
||||||
"set_warehouse",
|
"set_warehouse",
|
||||||
"rejected_warehouse",
|
"rejected_warehouse",
|
||||||
@@ -53,23 +41,36 @@
|
|||||||
"get_current_stock",
|
"get_current_stock",
|
||||||
"raw_material_details",
|
"raw_material_details",
|
||||||
"supplied_items",
|
"supplied_items",
|
||||||
"additional_costs_section",
|
|
||||||
"distribute_additional_costs_based_on",
|
|
||||||
"additional_costs",
|
|
||||||
"total_additional_costs",
|
|
||||||
"section_break_46",
|
"section_break_46",
|
||||||
"in_words",
|
"in_words",
|
||||||
"bill_no",
|
"bill_no",
|
||||||
"bill_date",
|
"bill_date",
|
||||||
|
"tab_addresses",
|
||||||
|
"supplier_address",
|
||||||
|
"contact_person",
|
||||||
|
"address_display",
|
||||||
|
"contact_display",
|
||||||
|
"contact_mobile",
|
||||||
|
"contact_email",
|
||||||
|
"col_break_address",
|
||||||
|
"shipping_address",
|
||||||
|
"shipping_address_display",
|
||||||
|
"billing_address",
|
||||||
|
"billing_address_display",
|
||||||
|
"tab_additional_costs",
|
||||||
|
"distribute_additional_costs_based_on",
|
||||||
|
"additional_costs",
|
||||||
|
"total_additional_costs",
|
||||||
|
"tab_other_info",
|
||||||
"more_info",
|
"more_info",
|
||||||
"status",
|
|
||||||
"column_break_39",
|
|
||||||
"per_returned",
|
|
||||||
"section_break_47",
|
|
||||||
"amended_from",
|
"amended_from",
|
||||||
"range",
|
"range",
|
||||||
"column_break4",
|
"column_break4",
|
||||||
"represents_company",
|
"represents_company",
|
||||||
|
"order_status_section",
|
||||||
|
"status",
|
||||||
|
"column_break_39",
|
||||||
|
"per_returned",
|
||||||
"subscription_detail",
|
"subscription_detail",
|
||||||
"auto_repeat",
|
"auto_repeat",
|
||||||
"printing_settings",
|
"printing_settings",
|
||||||
@@ -84,7 +85,8 @@
|
|||||||
"transporter_name",
|
"transporter_name",
|
||||||
"column_break5",
|
"column_break5",
|
||||||
"lr_no",
|
"lr_no",
|
||||||
"lr_date"
|
"lr_date",
|
||||||
|
"tab_connections"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -112,7 +114,7 @@
|
|||||||
"fieldname": "supplier",
|
"fieldname": "supplier",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"label": "Supplier",
|
"label": "Job Worker",
|
||||||
"options": "Supplier",
|
"options": "Supplier",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_width": "150px",
|
"print_width": "150px",
|
||||||
@@ -127,7 +129,7 @@
|
|||||||
"fieldname": "supplier_name",
|
"fieldname": "supplier_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
"label": "Supplier Name",
|
"label": "Job Worker Name",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -174,15 +176,14 @@
|
|||||||
"width": "150px"
|
"width": "150px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"fieldname": "tab_addresses",
|
||||||
"fieldname": "section_addresses",
|
"fieldtype": "Tab Break",
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Address and Contact"
|
"label": "Address and Contact"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "supplier_address",
|
"fieldname": "supplier_address",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Select Supplier Address",
|
"label": "Select Job Worker Address",
|
||||||
"options": "Address",
|
"options": "Address",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
@@ -269,7 +270,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "supplier_warehouse",
|
"fieldname": "supplier_warehouse",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Supplier Warehouse",
|
"label": "Job Worker Warehouse",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Warehouse",
|
"options": "Warehouse",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
@@ -414,6 +415,7 @@
|
|||||||
"width": "50%"
|
"width": "50%"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"collapsible": 1,
|
||||||
"fieldname": "subscription_detail",
|
"fieldname": "subscription_detail",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Auto Repeat Detail"
|
"label": "Auto Repeat Detail"
|
||||||
@@ -571,10 +573,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"fieldname": "section_break_47",
|
|
||||||
"fieldtype": "Section Break"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "accounting_dimensions_section",
|
"fieldname": "accounting_dimensions_section",
|
||||||
@@ -598,11 +596,9 @@
|
|||||||
"options": "Project"
|
"options": "Project"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
|
||||||
"collapsible_depends_on": "total_additional_costs",
|
|
||||||
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
|
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
|
||||||
"fieldname": "additional_costs_section",
|
"fieldname": "tab_additional_costs",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Tab Break",
|
||||||
"label": "Additional Costs"
|
"label": "Additional Costs"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -643,7 +639,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "supplier_delivery_note",
|
"fieldname": "supplier_delivery_note",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Supplier Delivery Note"
|
"label": "Job Worker Delivery Note"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "raw_materials_consumed_section",
|
"fieldname": "raw_materials_consumed_section",
|
||||||
@@ -658,6 +654,23 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "column_break_uinr",
|
"fieldname": "column_break_uinr",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_other_info",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Other Info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "order_status_section",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Order Status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tab_connections",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Connections",
|
||||||
|
"show_dashboard": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"in_create": 1,
|
"in_create": 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user