Compare commits

...

63 Commits

Author SHA1 Message Date
Ankush Menat
f784b17564 perf: avoid full table scan in sle count check 2023-07-31 20:06:46 +05:30
rohitwaghchaure
f83a100a8d fix: not able to make material request (#36416) 2023-07-31 19:13:23 +05:30
Devin Slauenwhite
e8eeeb16e2 fix: group item reorder by (warehouse, material_request_type) (#35818)
* fix: group item reorder by (warehouse, material_request_type)

* fix: update reorder error message

* chore: linter

* fix: correct error message

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>

* chore: linter

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-31 12:47:14 +00:00
xdlumertz
652398fad2 fix: Defined "Open" Status as default (#36421)
Defined "Open" Status as default of the child doctype (Quality Review Objective), because without it the main doctype (Quality Review) has "Passed" status.
This happens because in the "set_status" function, the status is updated according to the status of the child records.
2023-07-31 12:34:33 +00:00
Vimal
ce36d1f668 fix: job card suggest holiday as start date (#35958) 2023-07-31 17:51:31 +05:30
xdlumertz
05b07e098a fix: process_owner is not link User (#36420)
-Changed "fetch from" since field is not a binding field
-Change field "full_name" from Hidden to Read Only
2023-07-31 17:34:55 +05:30
Gursheen Kaur Anand
1ddfaa7605 fix: ignore cancelled gle in voucher-wise balance report (#36417)
fix: ignore cancelled gle
2023-07-31 14:29:20 +05:30
Deepesh Garg
c6d8f15b10 Merge pull request #36389 from AlexandreLumertz/patch-2
fix: Party type translation in error message
2023-07-30 18:42:01 +05:30
Deepesh Garg
c6b024c34b Merge pull request #36167 from barredterra/percentage-rounding
fix: rounding of percentage fields
2023-07-30 14:41:30 +05:30
Deepesh Garg
b38d300a92 Merge pull request #36320 from barredterra/german-translation-supplier
fix(Supplier): german translations
2023-07-30 14:40:15 +05:30
Deepesh Garg
eeddeeeeb3 Merge pull request #36369 from ashish-greycube/patch-11
fix: in payment_entry 'Unallocated Amount' cal is broken
2023-07-30 14:37:24 +05:30
Ankush Menat
c0642cf528 fix: only publish repost progress to doc subscriber (#36400)
Huge size of string gets blasted to everyone on site. Due to some memory
leak (cause unknown) till sockets are open the strings are also in
process' memory.

related https://github.com/frappe/frappe/issues/21863
2023-07-29 09:32:11 +00:00
rohitwaghchaure
b71dafd1f1 Merge pull request #36375 from ramonus/job-card-fix
fix: Job Card validation fixed when displaying total completed quantity
2023-07-29 15:01:16 +05:30
Deepesh Garg
caad4537c5 Merge pull request #36149 from GursheenK/balancing-accounting-dimensions
fix: make offsetting entry for acc dimensions in general ledger
2023-07-29 12:29:03 +05:30
Deepesh Garg
f999b75ed6 Merge branch 'develop' of https://github.com/frappe/erpnext into balancing-accounting-dimensions 2023-07-29 11:53:03 +05:30
Deepesh Garg
ecca9cb023 fix: Add company filters for account 2023-07-29 11:52:54 +05:30
Deepesh Garg
3173546d5c Merge pull request #36198 from HarryPaulo/fix-pricing-rule-on-point-of-sale
fix: inserting of items with pricing rule with qty range
2023-07-29 09:58:05 +05:30
ruthra kumar
3b58055410 refactor(test): introduce and make use of mixins in unit tests (#36382)
* refactor(test): create and use test mixin

* chore(test): replace get_user_default with variable
2023-07-28 21:02:21 +05:30
xdlumertz
bc470591ac fix: translate
fix: translate
2023-07-28 12:31:29 -03:00
rohitwaghchaure
ee7da639e7 Merge pull request #36380 from rohitwaghchaure/fixed-fieldtype-from-currency-to-flt
fix: change fieldtype from Currency to Float for the valuation rate in reports
2023-07-28 20:35:46 +05:30
s-aga-r
bc6cbb9e25 Merge pull request #36365 from AlexandreLumertz/patch-1
fix: removed "fetch_from"
2023-07-28 18:12:21 +05:30
xdlumertz
1c687a4afd fix: removed "fetch_from"
* fix: removed ("fetch_from": "goal.objective")
The field ended up being disabled because of this.
2023-07-28 18:10:45 +05:30
s-aga-r
d9ac7f9b87 Merge pull request #36221 from s-aga-r/FIX-ISS-23-24-02079
perf: use `LEFT JOIN` instead of `NOT EXISTS`
2023-07-28 17:51:24 +05:30
rohitwaghchaure
4e58503075 Merge pull request #36377 from rohitwaghchaure/fixed-pp-sub-assembly-available-items
fix: multiple issues related to Production Plan
2023-07-28 17:26:06 +05:30
Rohit Waghchaure
c82cb379a5 fix: change fieldtype from Currency to Float for the valuation rate in the stock report 2023-07-28 17:21:05 +05:30
s-aga-r
148d466ae5 fix: long queue process_boms_cost_level_wise 2023-07-28 17:05:17 +05:30
s-aga-r
58d867503b perf: use LEFT JOIN instead of NOT EXISTS 2023-07-28 16:44:41 +05:30
Rohit Waghchaure
1c2148b637 fix: multiple issues related to Production Plan 2023-07-28 15:38:00 +05:30
ramonalmato
49981fecc7 fix: Job Card validation fixed when displaying total completed quantity 2023-07-28 10:57:31 +02:00
Ankush Menat
e36c8ce5be perf: move project status reminder to hourly (#36372)
Only used for sending daily/weekly/bi-daily

[skip ci]
2023-07-28 12:53:22 +05:30
Ashish Shah
f9fa34ff40 fix: in payment_entry 'Unallocated Amount' cal is broken 2023-07-28 11:10:51 +05:30
ruthra kumar
50d294fd1e Merge pull request #36126 from ruthra-kumar/idx_issue_upon_journal_reconciliation
fix: incorrect `idx` on Journals after reconciliation
2023-07-28 09:33:29 +05:30
rohitwaghchaure
dac9fd64a8 Merge pull request #36328 from rohitwaghchaure/fixed-purchase-receipt-timeout-on-cancel-develop
fix: timeout error while cancelling the Purchase Receipt
2023-07-27 23:26:59 +05:30
Deepesh Garg
829387c2bc Merge pull request #36360 from deepeshgarg007/fy_default_dates
fix: Default year start and end dates in reports
2023-07-27 22:35:35 +05:30
Deepesh Garg
fff83bc847 Merge pull request #36092 from HarryPaulo/fix-paid-amount-payment-method-group
fix: paid_amount when the group is mode of payment
2023-07-27 21:21:52 +05:30
Deepesh Garg
ebdf1959fd Merge pull request #36254 from deepeshgarg007/remove_auto_repeat
fix: Remove auto repeat (subscription) button from Sales and Purchase documents
2023-07-27 21:18:03 +05:30
Deepesh Garg
a25f34c3d5 Merge pull request #36313 from barredterra/delete-root-item-group
fix(Item Group): allow root deletion
2023-07-27 21:14:44 +05:30
Deepesh Garg
2341061852 fix: Default year start and end date in reports 2023-07-27 21:03:32 +05:30
Deepesh Garg
4496a6760e fix: Default year start and end dates in reports 2023-07-27 20:54:55 +05:30
Deepesh Garg
dacf013170 Merge pull request #36347 from deepeshgarg007/ac_ignore
fix: Ignore account closing balance for financial statement
2023-07-27 19:54:01 +05:30
Deepesh Garg
523d2c38eb Merge pull request #36356 from frappe/revert-36249-default_dates
Revert "fix: Default year start and end dates in reports"
2023-07-27 19:15:00 +05:30
Gursheen Anand
4f9242d699 fix: dimension name in remark 2023-07-27 15:45:48 +05:30
Deepesh Garg
ccf1920a78 fix: Ignore account closing balance for financial statement 2023-07-27 15:40:36 +05:30
Rohit Waghchaure
1c2da92233 fix: timeout error while cancelling the Purchase Receipt 2023-07-26 13:03:23 +05:30
barredterra
3558c3d24e fix: german translations 2023-07-25 21:42:01 +02:00
barredterra
fd2c272bed fix(Item Group): allow root deletion
It was not possible to delete an empty, unused Item Group without any
children, if it was one of possibly multiple roots of the Item Group tree.
This fix allows deleting a root Item Group.
2023-07-25 17:01:57 +02:00
Deepesh Garg
4bb6db86f8 fix: Remove auto repeat (subscription) button from Sales and Purchase documents 2023-07-24 12:29:57 +05:30
HarryPaulo
4b4d828260 fix: inserting of items with pricing rule with qty range 2023-07-19 13:02:55 -03:00
Gursheen Anand
3f5afb9cac fix: reset dimension defaults when company changedin test 2023-07-19 18:15:59 +05:30
Gursheen Anand
23e56d3ec1 fix: clear dimension defaults after test 2023-07-19 16:07:32 +05:30
Gursheen Anand
e19a6f5dcb fix: fetch acc dimensions correctly when fieldname is different from name 2023-07-19 12:26:57 +05:30
Gursheen Anand
b3f6d991b5 fix: duplicate acc dimension in test 2023-07-19 12:02:26 +05:30
Gursheen Anand
77deac4fb9 test: PI offsetting entry for accounting dimension 2023-07-18 15:51:01 +05:30
Gursheen Anand
3a3ffa2307 fix: divide offsetting amount only when account exists 2023-07-18 12:51:09 +05:30
Gursheen Anand
1e1e4b93c1 fix: divide offsetting amount for multiple dimensions 2023-07-18 12:12:24 +05:30
barredterra
ecaf0aba3c fix: rounding of percentage fields
Always round with precision of 2
2023-07-17 16:45:11 +02:00
Gursheen Anand
ed3bef1840 fix: dict value for dimension for gl entries defined without the dimension 2023-07-17 18:40:52 +05:30
Gursheen Anand
4004427892 test: TB report balanced whenfiltered using acc dimension 2023-07-17 17:57:03 +05:30
Gursheen Anand
22ba12172f fix: make offsetting entry for all doctypes 2023-07-17 15:17:53 +05:30
Gursheen Anand
4e09de4db2 fix: fetch accounting dimension details specific to company 2023-07-17 11:47:33 +05:30
Gursheen Anand
d3759b3971 fix: make offsetting entry for acc dimensions 2023-07-15 19:32:56 +05:30
ruthra kumar
72f577aad2 fix: incorrect idx on JE's after reconciliation 2023-07-13 15:12:58 +05:30
HarryPaulo
2268f7db43 fix: paid_amount when the group is mode of payment 2023-07-11 11:35:37 -03:00
89 changed files with 969 additions and 376 deletions

View File

@@ -79,8 +79,8 @@ frappe.ui.form.on('Account', {
frm.add_custom_button(__('General Ledger'), function () {
frappe.route_options = {
"account": frm.doc.name,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"company": frm.doc.company
};
frappe.set_route("query-report", "General Ledger");

View File

@@ -194,8 +194,8 @@ frappe.treeview_settings["Account"] = {
click: function(node, btn) {
frappe.route_options = {
"account": node.label,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"from_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"to_date": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value()
};
frappe.set_route("query-report", "General Ledger");

View File

@@ -15,6 +15,17 @@ frappe.ui.form.on('Accounting Dimension', {
};
});
frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
return {
filters: {
company: d.company,
root_type: ["in", ["Asset", "Liability"]],
is_group: 0
}
}
});
if (!frm.is_new()) {
frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
frappe.set_route("List", frm.doc.document_type);

View File

@@ -39,6 +39,8 @@ class AccountingDimension(Document):
if not self.is_new():
self.validate_document_type_change()
self.validate_dimension_defaults()
def validate_document_type_change(self):
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
if doctype_before_save != self.document_type:
@@ -46,6 +48,14 @@ class AccountingDimension(Document):
message += _("Please create a new Accounting Dimension if required.")
frappe.throw(message)
def validate_dimension_defaults(self):
companies = []
for default in self.get("dimension_defaults"):
if default.company not in companies:
companies.append(default.company)
else:
frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company)))
def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)

View File

@@ -8,7 +8,10 @@
"reference_document",
"default_dimension",
"mandatory_for_bs",
"mandatory_for_pl"
"mandatory_for_pl",
"column_break_lqns",
"automatically_post_balancing_accounting_entry",
"offsetting_account"
],
"fields": [
{
@@ -50,6 +53,23 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Mandatory For Profit and Loss Account"
},
{
"default": "0",
"fieldname": "automatically_post_balancing_accounting_entry",
"fieldtype": "Check",
"label": "Automatically post balancing accounting entry"
},
{
"fieldname": "offsetting_account",
"fieldtype": "Link",
"label": "Offsetting Account",
"mandatory_depends_on": "eval: doc.automatically_post_balancing_accounting_entry",
"options": "Account"
},
{
"fieldname": "column_break_lqns",
"fieldtype": "Column Break"
}
],
"istable": 1,

View File

@@ -58,6 +58,7 @@
"closing_settings_tab",
"period_closing_settings_section",
"acc_frozen_upto",
"ignore_account_closing_balance",
"column_break_25",
"frozen_accounts_modifier",
"tab_break_dpet",
@@ -406,6 +407,13 @@
"fieldname": "enable_fuzzy_matching",
"fieldtype": "Check",
"label": "Enable Fuzzy Matching"
},
{
"default": "0",
"description": "Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing) ",
"fieldname": "ignore_account_closing_balance",
"fieldtype": "Check",
"label": "Ignore Account Closing Balance"
}
],
"icon": "icon-cog",
@@ -413,7 +421,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-06-15 16:35:45.123456",
"modified": "2023-07-27 15:05:34.000264",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -14,21 +14,32 @@ from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(Document):
def on_update(self):
frappe.clear_cache()
def validate(self):
frappe.db.set_default(
"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
)
old_doc = self.get_doc_before_save()
clear_cache = False
frappe.db.set_default(
"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
)
if old_doc.add_taxes_from_item_tax_template != self.add_taxes_from_item_tax_template:
frappe.db.set_default(
"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
)
clear_cache = True
if old_doc.enable_common_party_accounting != self.enable_common_party_accounting:
frappe.db.set_default(
"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
)
clear_cache = True
self.validate_stale_days()
self.enable_payment_schedule_in_print()
self.validate_pending_reposts()
if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print:
self.enable_payment_schedule_in_print()
if old_doc.acc_frozen_upto != self.acc_frozen_upto:
self.validate_pending_reposts()
if clear_cache:
frappe.clear_cache()
def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0:

View File

@@ -903,12 +903,12 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
unallocated_amount = (frm.doc.base_paid_amount + frm.doc.base_total_taxes_and_charges - (total_deductions
unallocated_amount = (frm.doc.base_paid_amount + flt(frm.doc.base_total_taxes_and_charges) - (total_deductions
+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
}
}

View File

@@ -101,12 +101,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
cur_frm.add_custom_button(__('Return / Debit Note'),
this.make_debit_note, __('Create'));
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
}
if (doc.outstanding_amount > 0 && !cint(doc.is_return) && !doc.on_hold) {

View File

@@ -1736,6 +1736,61 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
self.assertAlmostEqual(returned_inv.items[0].rate, rate)
def test_offsetting_entries_for_accounting_dimensions(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.report.trial_balance.test_trial_balance import (
clear_dimension_defaults,
create_accounting_dimension,
disable_dimension,
)
create_account(
account_name="Offsetting",
company="_Test Company",
parent_account="Temporary Accounts - _TC",
)
create_accounting_dimension(company="_Test Company", offsetting_account="Offsetting - _TC")
branch1 = frappe.new_doc("Branch")
branch1.branch = "Location 1"
branch1.insert(ignore_if_duplicate=True)
branch2 = frappe.new_doc("Branch")
branch2.branch = "Location 2"
branch2.insert(ignore_if_duplicate=True)
pi = make_purchase_invoice(
company="_Test Company",
customer="_Test Supplier",
do_not_save=True,
do_not_submit=True,
rate=1000,
price_list_rate=1000,
qty=1,
)
pi.branch = branch1.branch
pi.items[0].branch = branch2.branch
pi.save()
pi.submit()
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 1000, 0.0, nowdate(), branch2.branch],
["Creditors - _TC", 0.0, 1000, nowdate(), branch1.branch],
["Offsetting - _TC", 1000, 0.0, nowdate(), branch1.branch],
["Offsetting - _TC", 0.0, 1000, nowdate(), branch2.branch],
]
check_gl_entries(
self,
pi.name,
expected_gle,
nowdate(),
voucher_type="Purchase Invoice",
additional_columns=["branch"],
)
clear_dimension_defaults("Branch")
disable_dimension()
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(
@@ -1748,9 +1803,16 @@ def set_advance_flag(company, flag, default_account):
)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Purchase Invoice"):
def check_gl_entries(
doc,
voucher_no,
expected_gle,
posting_date,
voucher_type="Purchase Invoice",
additional_columns=None,
):
gl = frappe.qb.DocType("GL Entry")
q = (
query = (
frappe.qb.from_(gl)
.select(gl.account, gl.debit, gl.credit, gl.posting_date)
.where(
@@ -1761,7 +1823,12 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="
)
.orderby(gl.posting_date, gl.account, gl.creation)
)
gl_entries = q.run(as_dict=True)
if additional_columns:
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)
@@ -1769,6 +1836,12 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="
doc.assertEqual(expected_gle[i][2], gle.credit)
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
if additional_columns:
j = 4
for col in additional_columns:
doc.assertEqual(expected_gle[i][j], gle[col])
j += 1
def create_tax_witholding_category(category_name, company, account):
from erpnext.accounts.utils import get_fiscal_year

View File

@@ -443,7 +443,8 @@
"hidden": 1,
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "col_br_wh",
@@ -890,7 +891,8 @@
"label": "Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"depends_on": "eval:parent.update_stock == 1",
@@ -905,7 +907,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-07-04 17:22:21.501152",
"modified": "2023-07-26 12:54:53.178156",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -163,12 +163,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
cur_frm.cscript.make_maintenance_schedule();
}, __('Create'));
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
}
// Show buttons only when pos view is active

View File

@@ -1833,7 +1833,7 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
doc = frappe.get_doc(ref_doc, inter_company_reference)
ref_party = doc.supplier if doctype in ["Sales Invoice", "Sales Order"] else doc.customer
if not frappe.db.get_value(partytype, {"represents_company": doc.company}, "name") == party:
frappe.throw(_("Invalid {0} for Inter Company Transaction.").format(partytype))
frappe.throw(_("Invalid {0} for Inter Company Transaction.").format(_(partytype)))
if not frappe.get_cached_value(ref_partytype, ref_party, "represents_company") == company:
frappe.throw(_("Invalid Company for Inter Company Transaction."))
@@ -1847,7 +1847,7 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
if not company in companies:
frappe.throw(
_("{0} not allowed to transact with {1}. Please change the Company.").format(
partytype, company
_(partytype), company
)
)

View File

@@ -604,7 +604,8 @@
"hidden": 1,
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "col_break5",
@@ -894,13 +895,14 @@
"label": "Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-03-12 13:42:24.303113",
"modified": "2023-07-26 12:53:22.404057",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -28,6 +28,7 @@ def make_gl_entries(
):
if gl_map:
if not cancel:
make_acc_dimensions_offsetting_entry(gl_map)
validate_accounting_period(gl_map)
validate_disabled_accounts(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
@@ -51,6 +52,63 @@ def make_gl_entries(
make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def make_acc_dimensions_offsetting_entry(gl_map):
accounting_dimensions_to_offset = get_accounting_dimensions_for_offsetting_entry(
gl_map, gl_map[0].company
)
no_of_dimensions = len(accounting_dimensions_to_offset)
if no_of_dimensions == 0:
return
offsetting_entries = []
for gle in gl_map:
for dimension in accounting_dimensions_to_offset:
offsetting_entry = gle.copy()
debit = flt(gle.credit) / no_of_dimensions if gle.credit != 0 else 0
credit = flt(gle.debit) / no_of_dimensions if gle.debit != 0 else 0
offsetting_entry.update(
{
"account": dimension.offsetting_account,
"debit": debit,
"credit": credit,
"debit_in_account_currency": debit,
"credit_in_account_currency": credit,
"remarks": _("Offsetting for Accounting Dimension") + " - {0}".format(dimension.name),
"against_voucher": None,
}
)
offsetting_entry["against_voucher_type"] = None
offsetting_entries.append(offsetting_entry)
gl_map += offsetting_entries
def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
acc_dimension = frappe.qb.DocType("Accounting Dimension")
dimension_detail = frappe.qb.DocType("Accounting Dimension Detail")
acc_dimensions = (
frappe.qb.from_(acc_dimension)
.inner_join(dimension_detail)
.on(acc_dimension.name == dimension_detail.parent)
.select(acc_dimension.fieldname, acc_dimension.name, dimension_detail.offsetting_account)
.where(
(acc_dimension.disabled == 0)
& (dimension_detail.company == company)
& (dimension_detail.automatically_post_balancing_accounting_entry == 1)
)
).run(as_dict=True)
accounting_dimensions_to_offset = []
for acc_dimension in acc_dimensions:
values = set([entry.get(acc_dimension.fieldname) for entry in gl_map])
if len(values) > 1:
accounting_dimensions_to_offset.append(acc_dimension)
return accounting_dimensions_to_offset
def validate_disabled_accounts(gl_map):
accounts = [d.account for d in gl_map if d.account]

View File

@@ -15,14 +15,14 @@ frappe.query_reports["Asset Depreciations and Balances"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"reqd": 1
},
{

View File

@@ -7,7 +7,7 @@ frappe.query_reports["Bank Clearance Summary"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"width": "80"
},
{

View File

@@ -8,7 +8,7 @@ frappe.query_reports["Budget Variance Report"] = {
label: __("From Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1
},
{
@@ -16,7 +16,7 @@ frappe.query_reports["Budget Variance Report"] = {
label: __("To Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year,
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
reqd: 1
},
{

View File

@@ -2,6 +2,7 @@ import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate
from erpnext.accounts.doctype.account.test_account import create_account
@@ -10,16 +11,15 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
Deferred_Revenue_and_Expense_Report,
)
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.stock.doctype.item.test_item import create_item
class TestDeferredRevenueAndExpense(unittest.TestCase):
class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin):
@classmethod
def setUpClass(self):
clear_accounts_and_items()
create_company()
self.maxDiff = None
def clear_old_entries(self):
@@ -51,55 +51,58 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
if deferred_invoices:
qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()
def test_deferred_revenue(self):
self.clear_old_entries()
def setup_deferred_accounts_and_items(self):
# created deferred expense accounts, if not found
self.deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - " + self.company_abbr,
company=self.company,
)
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - _CD",
company="_Test Company DR",
self.deferred_expense_account = create_account(
account_name="Deferred Expense",
parent_account="Current Assets - " + self.company_abbr,
company=self.company,
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
def setUp(self):
self.create_company()
self.create_customer("_Test Customer")
self.create_supplier("_Test Furniture Supplier")
self.setup_deferred_accounts_and_items()
self.clear_old_entries()
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Customer DR"
customer.type = "Individual"
customer.insert()
def tearDown(self):
frappe.db.rollback()
item = create_item(
"_Test Internet Subscription",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_deferred_revenue(self):
self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_revenue_account
item.deferred_revenue_account = self.deferred_revenue_account
item.no_of_months = 3
item.save()
si = create_sales_invoice(
item=item.name,
company="_Test Company DR",
customer="_Test Customer DR",
debit_to="Debtors - _CD",
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date="2021-05-01",
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
)
si.items[0].income_account = "Sales - _CD"
si.items[0].income_account = self.income_account
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2021-05-01"
si.items[0].service_end_date = "2021-08-01"
si.items[0].deferred_revenue_account = deferred_revenue_account
si.items[0].income_account = "Sales - _CD"
si.items[0].deferred_revenue_account = self.deferred_revenue_account
si.save()
si.submit()
@@ -110,7 +113,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Income",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
@@ -120,7 +123,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -142,57 +145,36 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
]
self.assertEqual(report.period_total, expected)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_deferred_expense(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_expense_account = create_account(
account_name="Deferred Expense",
parent_account="Current Assets - _CD",
company="_Test Company DR",
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
supplier = create_supplier(
supplier_name="_Test Furniture Supplier", supplier_group="Local", supplier_type="Company"
)
supplier.save()
item = create_item(
"_Test Office Desk",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
self.create_item("_Test Office Desk", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_expense = 1
item.deferred_expense_account = deferred_expense_account
item.deferred_expense_account = self.deferred_expense_account
item.no_of_months_exp = 3
item.save()
pi = make_purchase_invoice(
item=item.name,
company="_Test Company DR",
supplier="_Test Furniture Supplier",
item=self.item,
company=self.company,
supplier=self.supplier,
is_return=False,
update_stock=False,
posting_date=frappe.utils.datetime.date(2021, 5, 1),
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
warehouse="All Warehouses - _CD",
warehouse=self.warehouse,
qty=1,
)
pi.set_posting_time = True
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2021-05-01"
pi.items[0].service_end_date = "2021-08-01"
pi.items[0].deferred_expense_account = deferred_expense_account
pi.items[0].expense_account = "Office Maintenance Expenses - _CD"
pi.items[0].deferred_expense_account = self.deferred_expense_account
pi.items[0].expense_account = self.expense_account
pi.save()
pi.submit()
@@ -203,7 +185,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Expense",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
@@ -213,7 +195,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -235,52 +217,31 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
]
self.assertEqual(report.period_total, expected)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_zero_months(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - _CD",
company="_Test Company DR",
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Customer DR"
customer.type = "Individual"
customer.insert()
item = create_item(
"_Test Internet Subscription",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_revenue_account
item.deferred_revenue_account = self.deferred_revenue_account
item.no_of_months = 0
item.save()
si = create_sales_invoice(
item=item.name,
company="_Test Company DR",
customer="_Test Customer DR",
debit_to="Debtors - _CD",
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date="2021-05-01",
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
)
si.items[0].enable_deferred_revenue = 1
si.items[0].income_account = "Sales - _CD"
si.items[0].deferred_revenue_account = deferred_revenue_account
si.items[0].income_account = "Sales - _CD"
si.items[0].income_account = self.income_account
si.items[0].deferred_revenue_account = self.deferred_revenue_account
si.save()
si.submit()
@@ -291,7 +252,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Income",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
@@ -301,7 +262,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -322,30 +283,3 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
{"key": "aug_2021", "total": 0, "actual": 0},
]
self.assertEqual(report.period_total, expected)
def create_company():
company = frappe.db.exists("Company", "_Test Company DR")
if not company:
company = frappe.new_doc("Company")
company.company_name = "_Test Company DR"
company.default_currency = "INR"
company.chart_of_accounts = "Standard"
company.insert()
def clear_accounts_and_items():
item = qb.DocType("Item")
account = qb.DocType("Account")
customer = qb.DocType("Customer")
supplier = qb.DocType("Supplier")
qb.from_(account).delete().where(
(account.account_name == "Deferred Revenue")
| (account.account_name == "Deferred Expense") & (account.company == "_Test Company DR")
).run()
qb.from_(item).delete().where(
(item.item_code == "_Test Internet Subscription") | (item.item_code == "_Test Office Rent")
).run()
qb.from_(customer).delete().where(customer.customer_name == "_Test Customer DR").run()
qb.from_(supplier).delete().where(supplier.supplier_name == "_Test Furniture Supplier").run()

View File

@@ -38,14 +38,14 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"reqd": 1
},
{

View File

@@ -429,11 +429,17 @@ def set_gl_entries_by_account(
if accounts_list:
# For balance sheet
if not from_date:
from_date = filters["period_start_date"]
ignore_closing_balances = frappe.db.get_single_value(
"Accounts Settings", "ignore_account_closing_balance"
)
if not from_date and not ignore_closing_balances:
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", from_date)},
filters={
"docstatus": 1,
"company": filters.company,
"posting_date": ("<", filters["period_start_date"]),
},
fields=["posting_date", "name"],
order_by="posting_date desc",
limit=1,

View File

@@ -15,14 +15,14 @@ frappe.query_reports["Gross Profit"] = {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"reqd": 1
},
{

View File

@@ -15,7 +15,7 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
fieldname:"to_date",

View File

@@ -50,20 +50,20 @@ def get_pos_entries(filters, group_by_field):
order_by = "p.posting_date"
select_mop_field, from_sales_invoice_payment, group_by_mop_condition = "", "", ""
if group_by_field == "mode_of_payment":
select_mop_field = ", sip.mode_of_payment"
select_mop_field = ", sip.mode_of_payment, sip.base_amount - IF(sip.type='Cash', p.change_amount, 0) as paid_amount"
from_sales_invoice_payment = ", `tabSales Invoice Payment` sip"
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount, 0) != 0 AND"
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount - IF(sip.type='Cash', p.change_amount, 0), 0) != 0 AND"
order_by += ", sip.mode_of_payment"
elif group_by_field:
order_by += ", p.{}".format(group_by_field)
select_mop_field = ", p.base_paid_amount - p.change_amount as paid_amount "
return frappe.db.sql(
"""
SELECT
p.posting_date, p.name as pos_invoice, p.pos_profile,
p.owner, p.base_grand_total as grand_total, p.base_paid_amount - p.change_amount as paid_amount,
p.customer, p.is_return {select_mop_field}
p.owner, p.customer, p.is_return, p.base_grand_total as grand_total {select_mop_field}
FROM
`tabPOS Invoice` p {from_sales_invoice_payment}
WHERE

View File

@@ -66,13 +66,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
},
{
"fieldname": "show_zero_values",

View File

@@ -0,0 +1,118 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today
from erpnext.accounts.report.trial_balance.trial_balance import execute
class TestTrialBalance(FrappeTestCase):
def setUp(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_fiscal_year
self.company = create_company()
create_cost_center(
cost_center_name="Test Cost Center",
company="Trial Balance Company",
parent_cost_center="Trial Balance Company - TBC",
)
create_account(
account_name="Offsetting",
company="Trial Balance Company",
parent_account="Temporary Accounts - TBC",
)
self.fiscal_year = get_fiscal_year(today(), company="Trial Balance Company")[0]
create_accounting_dimension()
def test_offsetting_entries_for_accounting_dimensions(self):
"""
Checks if Trial Balance Report is balanced when filtered using a particular Accounting Dimension
"""
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
frappe.db.sql("delete from `tabSales Invoice` where company='Trial Balance Company'")
frappe.db.sql("delete from `tabGL Entry` where company='Trial Balance Company'")
branch1 = frappe.new_doc("Branch")
branch1.branch = "Location 1"
branch1.insert(ignore_if_duplicate=True)
branch2 = frappe.new_doc("Branch")
branch2.branch = "Location 2"
branch2.insert(ignore_if_duplicate=True)
si = create_sales_invoice(
company=self.company,
debit_to="Debtors - TBC",
cost_center="Test Cost Center - TBC",
income_account="Sales - TBC",
do_not_submit=1,
)
si.branch = "Location 1"
si.items[0].branch = "Location 2"
si.save()
si.submit()
filters = frappe._dict(
{"company": self.company, "fiscal_year": self.fiscal_year, "branch": ["Location 1"]}
)
total_row = execute(filters)[1][-1]
self.assertEqual(total_row["debit"], total_row["credit"])
def tearDown(self):
clear_dimension_defaults("Branch")
disable_dimension()
def create_company(**args):
args = frappe._dict(args)
company = frappe.get_doc(
{
"doctype": "Company",
"company_name": args.company_name or "Trial Balance Company",
"country": args.country or "India",
"default_currency": args.currency or "INR",
}
)
company.insert(ignore_if_duplicate=True)
return company.name
def create_accounting_dimension(**args):
args = frappe._dict(args)
document_type = args.document_type or "Branch"
if frappe.db.exists("Accounting Dimension", document_type):
accounting_dimension = frappe.get_doc("Accounting Dimension", document_type)
accounting_dimension.disabled = 0
else:
accounting_dimension = frappe.new_doc("Accounting Dimension")
accounting_dimension.document_type = document_type
accounting_dimension.insert()
accounting_dimension.set("dimension_defaults", [])
accounting_dimension.append(
"dimension_defaults",
{
"company": args.company or "Trial Balance Company",
"automatically_post_balancing_accounting_entry": 1,
"offsetting_account": args.offsetting_account or "Offsetting - TBC",
},
)
accounting_dimension.save()
def disable_dimension(**args):
args = frappe._dict(args)
document_type = args.document_type or "Branch"
dimension = frappe.get_doc("Accounting Dimension", document_type)
dimension.disabled = 1
dimension.save()
def clear_dimension_defaults(dimension_name):
accounting_dimension = frappe.get_doc("Accounting Dimension", dimension_name)
accounting_dimension.dimension_defaults = []
accounting_dimension.save()

View File

@@ -37,13 +37,13 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
},
{
"fieldname": "cost_center",

View File

@@ -142,14 +142,20 @@ def get_opening_balances(filters):
def get_rootwise_opening_balances(filters, report_type):
gle = []
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)},
fields=["posting_date", "name"],
order_by="posting_date desc",
limit=1,
last_period_closing_voucher = ""
ignore_closing_balances = frappe.db.get_single_value(
"Accounts Settings", "ignore_account_closing_balance"
)
if not ignore_closing_balances:
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": filters.company, "posting_date": ("<", filters.from_date)},
fields=["posting_date", "name"],
order_by="posting_date desc",
limit=1,
)
accounting_dimensions = get_accounting_dimensions(as_list=False)
if last_period_closing_voucher:
@@ -253,7 +259,7 @@ def get_opening_balance(
lft, rgt = frappe.db.get_value("Cost Center", filters.cost_center, ["lft", "rgt"])
cost_center = frappe.qb.DocType("Cost Center")
opening_balance = opening_balance.where(
closing_balance.cost_center.in_(
closing_balance.cost_center.isin(
frappe.qb.from_(cost_center)
.select("name")
.where((cost_center.lft >= lft) & (cost_center.rgt <= rgt))

View File

@@ -36,13 +36,13 @@ frappe.query_reports["Trial Balance for Party"] = {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
},
{
"fieldname":"party_type",

View File

@@ -46,6 +46,7 @@ def get_data(filters):
.select(
gle.voucher_type, gle.voucher_no, Sum(gle.debit).as_("debit"), Sum(gle.credit).as_("credit")
)
.where(gle.is_cancelled == 0)
.groupby(gle.voucher_no)
)
query = apply_filters(query, filters, gle)

View File

@@ -0,0 +1,80 @@
import frappe
from erpnext.stock.doctype.item.test_item import create_item
class AccountsTestMixin:
def create_customer(self, customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):
customer = frappe.new_doc("Customer")
customer.customer_name = customer_name
customer.type = "Individual"
if currency:
customer.default_currency = currency
customer.save()
self.customer = customer.name
else:
self.customer = customer_name
def create_supplier(self, supplier_name, currency=None):
if not frappe.db.exists("Supplier", supplier_name):
supplier = frappe.new_doc("Supplier")
supplier.supplier_name = supplier_name
supplier.supplier_type = "Individual"
supplier.supplier_group = "Local"
if currency:
supplier.default_currency = currency
supplier.save()
self.supplier = supplier.name
else:
self.supplier = supplier_name
def create_item(self, item_name, is_stock=0, warehouse=None, company=None):
item = create_item(item_name, is_stock_item=is_stock, warehouse=warehouse, company=company)
self.item = item.name
def create_company(self, company_name="_Test Company", abbr="_TC"):
self.company_abbr = abbr
if frappe.db.exists("Company", company_name):
company = frappe.get_doc("Company", company_name)
else:
company = frappe.get_doc(
{
"doctype": "Company",
"company_name": company_name,
"country": "India",
"default_currency": "INR",
"create_chart_of_accounts_based_on": "Standard Template",
"chart_of_accounts": "Standard",
}
)
company = company.save()
self.company = company.name
self.cost_center = company.cost_center
self.warehouse = "Stores - " + abbr
self.finished_warehouse = "Finished Goods - " + abbr
self.income_account = "Sales - " + abbr
self.expense_account = "Cost of Goods Sold - " + abbr
self.debit_to = "Debtors - " + abbr
self.debit_usd = "Debtors USD - " + abbr
self.cash = "Cash - " + abbr
self.creditors = "Creditors - " + abbr
# create bank account
bank_account = "HDFC - " + abbr
if frappe.db.exists("Account", bank_account):
self.bank = bank_account
else:
bank_acc = frappe.get_doc(
{
"doctype": "Account",
"account_name": "HDFC",
"parent_account": "Bank Accounts - " + abbr,
"company": self.company,
}
)
bank_acc.save()
self.bank = bank_acc.name

View File

@@ -576,7 +576,11 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
# new row with references
new_row = journal_entry.append("accounts")
new_row.update((frappe.copy_doc(jv_detail)).as_dict())
# Copy field values into new row
[
new_row.set(field, jv_detail.get(field))
for field in frappe.get_meta("Journal Entry Account").get_fieldnames_with_value()
]
new_row.set(d["dr_or_cr"], d["allocated_amount"])
new_row.set(

View File

@@ -65,7 +65,7 @@ frappe.ui.form.on("Purchase Order", {
get_materials_from_supplier: function(frm) {
let po_details = [];
if (frm.doc.supplied_items && (frm.doc.per_received == 100 || frm.doc.status === 'Closed')) {
if (frm.doc.supplied_items && (flt(frm.doc.per_received, 2) == 100 || frm.doc.status === 'Closed')) {
frm.doc.supplied_items.forEach(d => {
if (d.total_supplied_qty && d.total_supplied_qty != d.consumed_qty) {
po_details.push(d.name)
@@ -184,7 +184,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
}
if(!in_list(["Closed", "Delivered"], doc.status)) {
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received, 2) < 100 && flt(this.frm.doc.per_billed, 2) < 100) {
// Don't add Update Items button if the PO is following the new subcontracting flow.
if (!(this.frm.doc.is_subcontracted && !this.frm.doc.is_old_subcontracting_flow)) {
this.frm.add_custom_button(__('Update Items'), () => {
@@ -198,7 +198,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
}
}
if (this.frm.has_perm("submit")) {
if(flt(doc.per_billed, 6) < 100 || flt(doc.per_received, 6) < 100) {
if(flt(doc.per_billed, 2) < 100 || flt(doc.per_received, 2) < 100) {
if (doc.status != "On Hold") {
this.frm.add_custom_button(__('Hold'), () => this.hold_purchase_order(), __("Status"));
} else{
@@ -221,7 +221,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
}
if(doc.status != "Closed") {
if (doc.status != "On Hold") {
if(flt(doc.per_received) < 100 && allow_receipt) {
if(flt(doc.per_received, 2) < 100 && allow_receipt) {
cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
if (doc.is_subcontracted) {
if (doc.is_old_subcontracting_flow) {
@@ -234,11 +234,11 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
}
}
}
if(flt(doc.per_billed) < 100)
if(flt(doc.per_billed, 2) < 100)
cur_frm.add_custom_button(__('Purchase Invoice'),
this.make_purchase_invoice, __('Create'));
if(flt(doc.per_billed) < 100 && doc.status != "Delivered") {
if(flt(doc.per_billed, 2) < 100 && doc.status != "Delivered") {
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
@@ -246,17 +246,11 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e
);
}
if(flt(doc.per_billed) < 100) {
if(flt(doc.per_billed, 2) < 100) {
this.frm.add_custom_button(__('Payment Request'),
function() { me.make_payment_request() }, __('Create'));
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this;
let internal = me.frm.doc.is_internal_supplier;

View File

@@ -27,13 +27,13 @@ frappe.query_reports["Procurement Tracker"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_end_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
},
]
}

View File

@@ -35,14 +35,14 @@ frappe.query_reports["Purchase Analytics"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_end_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1
},
{

View File

@@ -206,9 +206,11 @@ def post_process(doctype, data):
)
if doc.get("per_delivered"):
doc.status_percent += flt(doc.per_delivered)
doc.status_percent += flt(doc.per_delivered, 2)
doc.status_display.append(
_("Delivered") if doc.per_delivered == 100 else _("{0}% Delivered").format(doc.per_delivered)
_("Delivered")
if flt(doc.per_delivered, 2) == 100
else _("{0}% Delivered").format(doc.per_delivered)
)
if hasattr(doc, "set_indicator"):

View File

@@ -6,13 +6,13 @@ frappe.query_reports["Campaign Efficiency"] = {
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
}
]
};

View File

@@ -6,12 +6,12 @@
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
}
]};

View File

@@ -410,11 +410,11 @@ scheduler_events = {
],
},
"all": [
"erpnext.projects.doctype.project.project.project_status_update_reminder",
"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts",
],
"hourly": [
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
"erpnext.projects.doctype.project.project.project_status_update_reminder",
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status",
],

View File

@@ -93,6 +93,7 @@ class BOMUpdateLog(Document):
else:
frappe.enqueue(
method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
queue="long",
update_doc=self,
now=frappe.flags.in_test,
enqueue_after_commit=True,

View File

@@ -157,12 +157,19 @@ def get_next_higher_level_boms(
def get_leaf_boms() -> List[str]:
"Get BOMs that have no dependencies."
return frappe.db.sql_list(
"""select name from `tabBOM` bom
where docstatus=1 and is_active=1
and not exists(select bom_no from `tabBOM Item`
where parent=bom.name and bom_no !='')"""
)
bom = frappe.qb.DocType("BOM")
bom_item = frappe.qb.DocType("BOM Item")
boms = (
frappe.qb.from_(bom)
.left_join(bom_item)
.on((bom.name == bom_item.parent) & (bom_item.bom_no != ""))
.select(bom.name)
.where((bom.docstatus == 1) & (bom.is_active == 1) & (bom_item.bom_no.isnull()))
.distinct()
).run(pluck=True)
return boms
def _generate_dependence_map() -> defaultdict:

View File

@@ -544,12 +544,12 @@ class JobCard(Document):
if self.for_quantity and flt(total_completed_qty, precision) != flt(
self.for_quantity, precision
):
total_completed_qty = bold(_("Total Completed Qty"))
total_completed_qty_label = bold(_("Total Completed Qty"))
qty_to_manufacture = bold(_("Qty to Manufacture"))
frappe.throw(
_("The {0} ({1}) must be equal to {2} ({3})").format(
total_completed_qty,
total_completed_qty_label,
bold(flt(total_completed_qty, precision)),
qty_to_manufacture,
bold(self.for_quantity),

View File

@@ -9,19 +9,25 @@ frappe.ui.form.on('Production Plan', {
item.temporary_name = item.name;
});
},
setup(frm) {
frm.trigger("setup_queries");
frm.custom_make_buttons = {
'Work Order': 'Work Order / Subcontract PO',
'Material Request': 'Material Request',
};
},
frm.fields_dict['po_items'].grid.get_field('warehouse').get_query = function(doc) {
setup_queries(frm) {
frm.set_query("sales_order", "sales_orders", () => {
return {
query: "erpnext.manufacturing.doctype.production_plan.production_plan.sales_order_query",
filters: {
company: doc.company
company: frm.doc.company,
}
}
}
});
frm.set_query('for_warehouse', function(doc) {
return {
@@ -42,32 +48,40 @@ frappe.ui.form.on('Production Plan', {
};
});
frm.fields_dict['po_items'].grid.get_field('item_code').get_query = function(doc) {
frm.set_query("item_code", "po_items", (doc, cdt, cdn) => {
return {
query: "erpnext.controllers.queries.item_query",
filters:{
'is_stock_item': 1,
}
}
}
});
frm.fields_dict['po_items'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) {
frm.set_query("bom_no", "po_items", (doc, cdt, cdn) => {
var d = locals[cdt][cdn];
if (d.item_code) {
return {
query: "erpnext.controllers.queries.bom",
filters:{'item': cstr(d.item_code), 'docstatus': 1}
filters:{'item': d.item_code, 'docstatus': 1}
}
} else frappe.msgprint(__("Please enter Item first"));
}
});
frm.fields_dict['mr_items'].grid.get_field('warehouse').get_query = function(doc) {
frm.set_query("warehouse", "mr_items", (doc) => {
return {
filters: {
company: doc.company
}
}
}
});
frm.set_query("warehouse", "po_items", (doc) => {
return {
filters: {
company: doc.company
}
}
});
},
refresh(frm) {
@@ -436,7 +450,7 @@ frappe.ui.form.on("Production Plan Item", {
}
});
}
}
},
});
frappe.ui.form.on("Material Request Plan Item", {
@@ -467,31 +481,36 @@ frappe.ui.form.on("Material Request Plan Item", {
frappe.ui.form.on("Production Plan Sales Order", {
sales_order(frm, cdt, cdn) {
const { sales_order } = locals[cdt][cdn];
let row = locals[cdt][cdn];
const sales_order = row.sales_order;
if (!sales_order) {
return;
}
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details",
args: { sales_order },
callback(r) {
const {transaction_date, customer, grand_total} = r.message;
frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date);
frappe.model.set_value(cdt, cdn, 'customer', customer);
frappe.model.set_value(cdt, cdn, 'grand_total', grand_total);
}
});
if (row.sales_order) {
frm.call({
method: "validate_sales_orders",
doc: frm.doc,
args: {
sales_order: row.sales_order,
},
callback(r) {
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details",
args: { sales_order },
callback(r) {
const {transaction_date, customer, grand_total} = r.message;
frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date);
frappe.model.set_value(cdt, cdn, 'customer', customer);
frappe.model.set_value(cdt, cdn, 'grand_total', grand_total);
}
});
}
});
}
}
});
cur_frm.fields_dict['sales_orders'].grid.get_field("sales_order").get_query = function() {
return{
filters: [
['Sales Order','docstatus', '=' ,1]
]
}
};
frappe.tour['Production Plan'] = [
{
fieldname: "get_items_from",

View File

@@ -228,10 +228,10 @@
},
{
"default": "0",
"description": "To know more about projected quantity, <a href=\"https://erpnext.com/docs/user/manual/en/stock/projected-quantity\" style=\"text-decoration: underline;\" target=\"_blank\">click here</a>.",
"description": "If enabled, the system won't create material requests for the available items.",
"fieldname": "ignore_existing_ordered_qty",
"fieldtype": "Check",
"label": "Ignore Existing Projected Quantity"
"label": "Ignore Available Stock"
},
{
"fieldname": "column_break_25",
@@ -339,7 +339,7 @@
"depends_on": "eval:doc.get_items_from == 'Sales Order'",
"fieldname": "combine_items",
"fieldtype": "Check",
"label": "Consolidate Items"
"label": "Consolidate Sales Order Items"
},
{
"fieldname": "section_break_25",
@@ -399,7 +399,7 @@
},
{
"default": "0",
"description": "System consider the projected quantity to check available or will be available sub-assembly items ",
"description": "If this checkbox is enabled, then the system won\u2019t run the MRP for the available sub-assembly items.",
"fieldname": "skip_available_sub_assembly_item",
"fieldtype": "Check",
"label": "Skip Available Sub Assembly Items"
@@ -422,7 +422,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-05-22 23:36:31.770517",
"modified": "2023-07-28 13:37:43.926686",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",

View File

@@ -39,6 +39,36 @@ class ProductionPlan(Document):
self.set_status()
self._rename_temporary_references()
validate_uom_is_integer(self, "stock_uom", "planned_qty")
self.validate_sales_orders()
@frappe.whitelist()
def validate_sales_orders(self, sales_order=None):
sales_orders = []
if sales_order:
sales_orders.append(sales_order)
else:
sales_orders = [row.sales_order for row in self.sales_orders if row.sales_order]
data = sales_order_query(filters={"company": self.company, "sales_orders": sales_orders})
title = _("Production Plan Already Submitted")
if not data:
msg = _("No items are available in the sales order {0} for production").format(sales_orders[0])
if len(sales_orders) > 1:
sales_orders = ", ".join(sales_orders)
msg = _("No items are available in sales orders {0} for production").format(sales_orders)
frappe.throw(msg, title=title)
data = [d[0] for d in data]
for sales_order in sales_orders:
if sales_order not in data:
frappe.throw(
_("No items are available in the sales order {0} for production").format(sales_order),
title=title,
)
def set_pending_qty_in_row_without_reference(self):
"Set Pending Qty in independent rows (not from SO or MR)."
@@ -205,6 +235,7 @@ class ProductionPlan(Document):
).as_("pending_qty"),
so_item.description,
so_item.name,
so_item.bom_no,
)
.distinct()
.where(
@@ -342,7 +373,7 @@ class ProductionPlan(Document):
"item_code": data.item_code,
"description": data.description or item_details.description,
"stock_uom": item_details and item_details.stock_uom or "",
"bom_no": item_details and item_details.bom_no or "",
"bom_no": data.bom_no or item_details and item_details.bom_no or "",
"planned_qty": data.pending_qty,
"pending_qty": data.pending_qty,
"planned_start_date": now_datetime(),
@@ -401,11 +432,50 @@ class ProductionPlan(Document):
def on_submit(self):
self.update_bin_qty()
self.update_sales_order()
def on_cancel(self):
self.db_set("status", "Cancelled")
self.delete_draft_work_order()
self.update_bin_qty()
self.update_sales_order()
def update_sales_order(self):
sales_orders = [row.sales_order for row in self.po_items if row.sales_order]
if sales_orders:
so_wise_planned_qty = self.get_so_wise_planned_qty(sales_orders)
for row in self.po_items:
if not row.sales_order and not row.sales_order_item:
continue
key = (row.sales_order, row.sales_order_item)
frappe.db.set_value(
"Sales Order Item",
row.sales_order_item,
"production_plan_qty",
flt(so_wise_planned_qty.get(key)),
)
@staticmethod
def get_so_wise_planned_qty(sales_orders):
so_wise_planned_qty = frappe._dict()
data = frappe.get_all(
"Production Plan Item",
fields=["sales_order", "sales_order_item", "SUM(planned_qty) as qty"],
filters={
"sales_order": ("in", sales_orders),
"docstatus": 1,
"sales_order_item": ("is", "set"),
},
group_by="sales_order, sales_order_item",
)
for row in data:
key = (row.sales_order, row.sales_order_item)
so_wise_planned_qty[key] = row.qty
return so_wise_planned_qty
def update_bin_qty(self):
for d in self.mr_items:
@@ -719,6 +789,9 @@ class ProductionPlan(Document):
sub_assembly_items_store = [] # temporary store to process all subassembly items
for row in self.po_items:
if self.skip_available_sub_assembly_item and not row.warehouse:
frappe.throw(_("Row #{0}: Please select the FG Warehouse in Assembly Items").format(row.idx))
if not row.item_code:
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
@@ -1142,7 +1215,7 @@ def get_sales_orders(self):
& (so.docstatus == 1)
& (so.status.notin(["Stopped", "Closed"]))
& (so.company == self.company)
& (so_item.qty > so_item.work_order_qty)
& (so_item.qty > so_item.production_plan_qty)
)
)
@@ -1566,7 +1639,6 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
def get_raw_materials_of_sub_assembly_items(
item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
):
bei = frappe.qb.DocType("BOM Item")
bom = frappe.qb.DocType("BOM")
item = frappe.qb.DocType("Item")
@@ -1609,7 +1681,10 @@ def get_raw_materials_of_sub_assembly_items(
for item in items:
key = (item.item_code, item.bom_no)
if item.bom_no and key in sub_assembly_items:
if item.bom_no and key not in sub_assembly_items:
continue
if item.bom_no:
planned_qty = flt(sub_assembly_items[key])
get_raw_materials_of_sub_assembly_items(
item_details,
@@ -1626,3 +1701,42 @@ def get_raw_materials_of_sub_assembly_items(
item_details.setdefault(item.get("item_code"), item)
return item_details
@frappe.whitelist()
def sales_order_query(
doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None
):
frappe.has_permission("Production Plan", throw=True)
if not filters:
filters = {}
so_table = frappe.qb.DocType("Sales Order")
table = frappe.qb.DocType("Sales Order Item")
query = (
frappe.qb.from_(so_table)
.join(table)
.on(table.parent == so_table.name)
.select(table.parent)
.distinct()
.where((table.qty > table.production_plan_qty) & (table.docstatus == 1))
)
if filters.get("company"):
query = query.where(so_table.company == filters.get("company"))
if filters.get("sales_orders"):
query = query.where(so_table.name.isin(filters.get("sales_orders")))
if txt:
query = query.where(table.item_code.like(f"{txt}%"))
if page_len:
query = query.limit(page_len)
if start:
query = query.offset(start)
return query.run()

View File

@@ -225,6 +225,102 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(sales_orders, [])
def test_donot_allow_to_make_multiple_pp_against_same_so(self):
item = "Test SO Production Item 1"
create_item(item)
raw_material = "Test SO RM Production Item 1"
create_item(raw_material)
if not frappe.db.get_value("BOM", {"item": item}):
make_bom(item=item, raw_materials=[raw_material])
so = make_sales_order(item_code=item, qty=4)
pln = frappe.new_doc("Production Plan")
pln.company = so.company
pln.get_items_from = "Sales Order"
pln.append(
"sales_orders",
{
"sales_order": so.name,
"sales_order_date": so.transaction_date,
"customer": so.customer,
"grand_total": so.grand_total,
},
)
pln.get_so_items()
pln.submit()
pln = frappe.new_doc("Production Plan")
pln.company = so.company
pln.get_items_from = "Sales Order"
pln.append(
"sales_orders",
{
"sales_order": so.name,
"sales_order_date": so.transaction_date,
"customer": so.customer,
"grand_total": so.grand_total,
},
)
pln.get_so_items()
self.assertRaises(frappe.ValidationError, pln.save)
def test_so_based_bill_of_material(self):
item = "Test SO Production Item 1"
create_item(item)
raw_material = "Test SO RM Production Item 1"
create_item(raw_material)
bom1 = make_bom(item=item, raw_materials=[raw_material])
so = make_sales_order(item_code=item, qty=4)
# Create new BOM and assign to new sales order
bom2 = make_bom(item=item, raw_materials=[raw_material])
so2 = make_sales_order(item_code=item, qty=4)
pln1 = frappe.new_doc("Production Plan")
pln1.company = so.company
pln1.get_items_from = "Sales Order"
pln1.append(
"sales_orders",
{
"sales_order": so.name,
"sales_order_date": so.transaction_date,
"customer": so.customer,
"grand_total": so.grand_total,
},
)
pln1.get_so_items()
self.assertEqual(pln1.po_items[0].bom_no, bom1.name)
pln2 = frappe.new_doc("Production Plan")
pln2.company = so2.company
pln2.get_items_from = "Sales Order"
pln2.append(
"sales_orders",
{
"sales_order": so2.name,
"sales_order_date": so2.transaction_date,
"customer": so2.customer,
"grand_total": so2.grand_total,
},
)
pln2.get_so_items()
self.assertEqual(pln2.po_items[0].bom_no, bom2.name)
def test_production_plan_combine_items(self):
"Test combining FG items in Production Plan."
item = "Test Production Item 1"

View File

@@ -114,7 +114,7 @@ class Workstation(Document):
if schedule_date in tuple(get_holidays(self.holiday_list)):
schedule_date = add_days(schedule_date, 1)
self.validate_workstation_holiday(schedule_date, skip_holiday_list_check=True)
return self.validate_workstation_holiday(schedule_date, skip_holiday_list_check=True)
return schedule_date

View File

@@ -37,14 +37,14 @@ frappe.query_reports["Job Card Summary"] = {
label: __("From Posting Date"),
fieldname:"from_date",
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
reqd: 1
},
{
label: __("To Posting Date"),
fieldname:"to_date",
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_end_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1,
},
{

View File

@@ -16,14 +16,14 @@ frappe.query_reports["Production Analytics"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_end_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1
},
{

View File

@@ -6,7 +6,9 @@ def execute():
frappe.reload_doc("selling", "doctype", "sales_order_item")
for doctype in ["Sales Order", "Material Request"]:
condition = " and child_doc.stock_qty > child_doc.produced_qty and doc.per_delivered < 100"
condition = (
" and child_doc.stock_qty > child_doc.produced_qty and ROUND(doc.per_delivered, 2) < 100"
)
if doctype == "Material Request":
condition = " and doc.per_ordered < 100 and doc.material_request_type = 'Manufacture'"

View File

@@ -28,7 +28,7 @@ erpnext.get_purchase_trends_filters = function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options":'Fiscal Year',
"default": frappe.sys_defaults.fiscal_year
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today())
},
{
"fieldname":"period_based_on",

View File

@@ -48,7 +48,7 @@ erpnext.get_sales_trends_filters = function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options":'Fiscal Year',
"default": frappe.sys_defaults.fiscal_year
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
},
{
"fieldname":"company",

View File

@@ -400,7 +400,7 @@ $.extend(erpnext.utils, {
});
},
get_fiscal_year: function(date) {
get_fiscal_year: function(date, with_dates=false) {
if(!date) {
date = frappe.datetime.get_today();
}
@@ -414,7 +414,10 @@ $.extend(erpnext.utils, {
async: false,
callback: function(r) {
if (r.message) {
fiscal_year = r.message[0];
if (with_dates)
fiscal_year = r.message;
else
fiscal_year = r.message[0];
}
}
});

View File

@@ -62,10 +62,10 @@
"fieldtype": "Column Break"
},
{
"fetch_from": "process_owner.full_name",
"fetch_from": "procedure.process_owner_full_name",
"fieldname": "full_name",
"fieldtype": "Data",
"hidden": 1,
"read_only": 1,
"label": "Full Name"
},
{
@@ -81,7 +81,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-02-26 15:27:47.247814",
"modified": "2023-07-31 08:10:47.247814",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Non Conformance",

View File

@@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "format:{####}",
"creation": "2019-05-26 15:03:43.996455",
"doctype": "DocType",
@@ -12,7 +13,6 @@
],
"fields": [
{
"fetch_from": "goal.objective",
"fieldname": "objective",
"fieldtype": "Text",
"in_list_view": 1,
@@ -38,14 +38,17 @@
}
],
"istable": 1,
"modified": "2019-05-26 16:12:54.832058",
"links": [],
"modified": "2023-07-28 18:10:23.351246",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Goal Objective",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -56,6 +56,7 @@
"fieldtype": "Column Break"
},
{
"default": "Open",
"columns": 2,
"fieldname": "status",
"fieldtype": "Select",
@@ -67,7 +68,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-10-27 16:28:20.908637",
"modified": "2023-07-31 09:20:20.908637",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Review Objective",
@@ -76,4 +77,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -126,12 +126,6 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext.
});
}
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}

View File

@@ -52,7 +52,7 @@ frappe.ui.form.on("Sales Order", {
refresh: function(frm) {
if(frm.doc.docstatus === 1) {
if (frm.doc.status !== 'Closed' && flt(frm.doc.per_delivered, 6) < 100 && flt(frm.doc.per_billed, 6) < 100) {
if (frm.doc.status !== 'Closed' && flt(frm.doc.per_delivered, 2) < 100 && flt(frm.doc.per_billed, 2) < 100) {
frm.add_custom_button(__('Update Items'), () => {
erpnext.utils.update_child_items({
frm: frm,
@@ -309,7 +309,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
me.frm.cscript.update_status('Resume', 'Draft')
}, __("Status"));
if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
// close
this.frm.add_custom_button(__('Close'), () => this.close_sales_order(), __("Status"))
}
@@ -327,7 +327,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
&& !this.frm.doc.skip_delivery_note
if (this.frm.has_perm("submit")) {
if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) {
if(flt(doc.per_delivered, 2) < 100 || flt(doc.per_billed, 2) < 100) {
// hold
this.frm.add_custom_button(__('Hold'), () => this.hold_sales_order(), __("Status"))
// close
@@ -335,7 +335,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
}
}
if (flt(doc.per_picked, 6) < 100 && flt(doc.per_delivered, 6) < 100) {
if (flt(doc.per_picked, 2) < 100 && flt(doc.per_delivered, 2) < 100) {
this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create'));
}
@@ -345,18 +345,18 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
const order_is_a_custom_sale = ["Sales", "Shopping Cart", "Maintenance"].indexOf(doc.order_type) === -1;
// delivery note
if(flt(doc.per_delivered, 6) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery) {
if(flt(doc.per_delivered, 2) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery) {
this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create'));
this.frm.add_custom_button(__('Work Order'), () => this.make_work_order(), __('Create'));
}
// sales invoice
if(flt(doc.per_billed, 6) < 100) {
if(flt(doc.per_billed, 2) < 100) {
this.frm.add_custom_button(__('Sales Invoice'), () => me.make_sales_invoice(), __('Create'));
}
// material request
if(!doc.order_type || (order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered, 6) < 100) {
if(!doc.order_type || (order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered, 2) < 100) {
this.frm.add_custom_button(__('Material Request'), () => this.make_material_request(), __('Create'));
this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create'));
}
@@ -377,12 +377,6 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
this.frm.add_custom_button(__('Project'), () => this.make_project(), __('Create'));
}
if(!doc.auto_repeat) {
this.frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this;
let internal = me.frm.doc.is_internal_customer;

View File

@@ -733,7 +733,7 @@ def make_material_request(source_name, target_doc=None):
# qty is for packed items, because packed items don't have stock_qty field
qty = source.get("qty")
target.project = source_parent.project
target.qty = qty - requested_item_qty.get(source.name, 0) - source.delivered_qty
target.qty = qty - requested_item_qty.get(source.name, 0) - flt(source.get("delivered_qty"))
target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
args = target.as_dict().copy()
@@ -767,7 +767,7 @@ def make_material_request(source_name, target_doc=None):
"doctype": "Material Request Item",
"field_map": {"name": "sales_order_item", "parent": "sales_order"},
"condition": lambda doc: not frappe.db.exists("Product Bundle", doc.item_code)
and (doc.stock_qty - doc.delivered_qty) > requested_item_qty.get(doc.name, 0),
and (doc.stock_qty - flt(doc.get("delivered_qty"))) > requested_item_qty.get(doc.name, 0),
"postprocess": update_item,
},
},

View File

@@ -10,7 +10,7 @@ frappe.listview_settings['Sales Order'] = {
return [__("On Hold"), "orange", "status,=,On Hold"];
} else if (doc.status === "Completed") {
return [__("Completed"), "green", "status,=,Completed"];
} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 6) < 100) {
} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) {
if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
// not delivered & overdue
return [__("Overdue"), "red",
@@ -19,7 +19,7 @@ frappe.listview_settings['Sales Order'] = {
// not delivered (zeroount order)
return [__("To Deliver"), "orange",
"per_delivered,<,100|grand_total,=,0|status,!=,Closed"];
} else if (flt(doc.per_billed, 6) < 100) {
} else if (flt(doc.per_billed, 2) < 100) {
// not delivered & not billed
return [__("To Deliver and Bill"), "orange",
"per_delivered,<,100|per_billed,<,100|status,!=,Closed"];
@@ -28,12 +28,12 @@ frappe.listview_settings['Sales Order'] = {
return [__("To Deliver"), "orange",
"per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
}
} else if ((flt(doc.per_delivered, 6) === 100) && flt(doc.grand_total) !== 0
&& flt(doc.per_billed, 6) < 100) {
} else if ((flt(doc.per_delivered, 2) === 100) && flt(doc.grand_total) !== 0
&& flt(doc.per_billed, 2) < 100) {
// to bill
return [__("To Bill"), "orange",
"per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
} else if (doc.skip_delivery_note && flt(doc.per_billed, 6) < 100){
} else if (doc.skip_delivery_note && flt(doc.per_billed, 2) < 100){
return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"];
}
},

View File

@@ -549,6 +549,26 @@ class TestSalesOrder(FrappeTestCase):
workflow.is_active = 0
workflow.save()
def test_material_request_for_product_bundle(self):
# Create the Material Request from the sales order for the Packing Items
# Check whether the material request has the correct packing item or not.
if not frappe.db.exists("Item", "_Test Product Bundle Item New 1"):
bundle_item = make_item("_Test Product Bundle Item New 1", {"is_stock_item": 0})
bundle_item.append(
"item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"}
)
bundle_item.save(ignore_permissions=True)
make_item("_Packed Item New 2", {"is_stock_item": 1})
make_product_bundle("_Test Product Bundle Item New 1", ["_Packed Item New 2"], 2)
so = make_sales_order(
item_code="_Test Product Bundle Item New 1",
)
mr = make_material_request(so.name)
self.assertEqual(mr.items[0].item_code, "_Packed Item New 2")
def test_bin_details_of_packed_item(self):
# test Update Items with product bundle
if not frappe.db.exists("Item", "_Test Product Bundle Item New"):

View File

@@ -84,6 +84,7 @@
"actual_qty",
"ordered_qty",
"planned_qty",
"production_plan_qty",
"column_break_69",
"work_order_qty",
"delivered_qty",
@@ -882,12 +883,19 @@
"print_hide": 1,
"read_only": 1,
"report_hide": 1
},
{
"fieldname": "production_plan_qty",
"fieldtype": "Float",
"label": "Production Plan Qty",
"no_copy": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-04-04 10:44:05.707488",
"modified": "2023-07-28 14:56:42.031636",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order Item",

View File

@@ -605,7 +605,6 @@ erpnext.PointOfSale.Controller = class {
i => i.item_code === item_code
&& (!has_batch_no || (has_batch_no && i.batch_no === batch_no))
&& (i.uom === uom)
&& (i.rate == rate)
);
}

View File

@@ -23,14 +23,14 @@ frappe.query_reports["Customer Acquisition and Loyalty"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
"reqd": 1
}
],

View File

@@ -35,14 +35,14 @@ frappe.query_reports["Sales Analytics"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
reqd: 1
},
{
fieldname:"to_date",
label: __("To Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_end_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
reqd: 1
},
{

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Partner Target Variance based on Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today())
},
{
fieldname: "doctype",

View File

@@ -22,7 +22,7 @@ frappe.query_reports["Sales Person Commission Summary"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
fieldname:"to_date",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
},
{
fieldname: "doctype",

View File

@@ -20,7 +20,7 @@ frappe.query_reports["Sales Person-wise Transaction Summary"] = {
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
default: frappe.defaults.get_user_default("year_start_date"),
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
fieldname:"to_date",

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Territory Target Variance Based On Item Group"] = {
label: __("Fiscal Year"),
fieldtype: "Link",
options: "Fiscal Year",
default: frappe.sys_defaults.fiscal_year
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today())
},
{
fieldname: "doctype",

View File

@@ -73,7 +73,7 @@ class ItemGroup(NestedSet, WebsiteGenerator):
return self.route
def on_trash(self):
NestedSet.on_trash(self)
NestedSet.on_trash(self, allow_root_deletion=True)
WebsiteGenerator.on_trash(self)
self.delete_child_item_groups_key()

View File

@@ -235,12 +235,6 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends erpn
__("Status"))
}
erpnext.stock.delivery_note.set_print_hide(doc, dt, dn);
if(doc.docstatus==1 && !doc.is_return && !doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
}, __('Create'))
}
}
make_shipment() {

View File

@@ -854,7 +854,8 @@
"label": "Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"fieldname": "pick_serial_and_batch",
@@ -884,14 +885,15 @@
"hidden": 1,
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-05-02 21:05:14.175640",
"modified": "2023-07-26 12:53:49.357171",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note Item",

View File

@@ -395,16 +395,16 @@ class Item(Document):
def validate_warehouse_for_reorder(self):
"""Validate Reorder level table for duplicate and conditional mandatory"""
warehouse = []
warehouse_material_request_type: list[tuple[str, str]] = []
for d in self.get("reorder_levels"):
if not d.warehouse_group:
d.warehouse_group = d.warehouse
if d.get("warehouse") and d.get("warehouse") not in warehouse:
warehouse += [d.get("warehouse")]
if (d.get("warehouse"), d.get("material_request_type")) not in warehouse_material_request_type:
warehouse_material_request_type += [(d.get("warehouse"), d.get("material_request_type"))]
else:
frappe.throw(
_("Row {0}: An Reorder entry already exists for this warehouse {1}").format(
d.idx, d.warehouse
_("Row #{0}: A reorder entry already exists for warehouse {1} with reorder type {2}.").format(
d.idx, d.warehouse, d.material_request_type
),
DuplicateReorderRows,
)

View File

@@ -83,7 +83,8 @@
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "column_break_2",
@@ -199,7 +200,8 @@
"label": "Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"fieldname": "pick_serial_and_batch",
@@ -209,7 +211,7 @@
],
"istable": 1,
"links": [],
"modified": "2023-06-16 14:05:51.719959",
"modified": "2023-07-26 12:54:15.785962",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",

View File

@@ -189,12 +189,6 @@ erpnext.stock.PurchaseReceiptController = class PurchaseReceiptController extend
}
cur_frm.add_custom_button(__('Retention Stock Entry'), this.make_retention_stock_entry, __('Create'));
if(!this.frm.doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(me.frm.doc.doctype, me.frm.doc.name)
}, __('Create'))
}
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}
}

View File

@@ -502,7 +502,6 @@
{
"fieldname": "rejected_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Rejected Warehouse",
"no_copy": 1,
"oldfieldname": "rejected_warehouse",
@@ -1003,7 +1002,8 @@
"label": "Serial and Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"depends_on": "eval:parent.is_old_subcontracting_flow",
@@ -1028,7 +1028,8 @@
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "rejected_serial_and_batch_bundle",
@@ -1059,7 +1060,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-07-04 17:22:02.830029",
"modified": "2023-07-26 12:55:15.234477",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",

View File

@@ -49,7 +49,8 @@
"in_list_view": 1,
"label": "Company",
"options": "Company",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"fetch_from": "item_code.item_group",
@@ -78,7 +79,8 @@
"in_standard_filter": 1,
"label": "Item Code",
"options": "Item",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"fetch_from": "item_code.item_name",
@@ -180,14 +182,16 @@
"in_standard_filter": 1,
"label": "Warehouse",
"mandatory_depends_on": "eval:doc.type_of_transaction != \"Maintenance\"",
"options": "Warehouse"
"options": "Warehouse",
"search_index": 1
},
{
"fieldname": "type_of_transaction",
"fieldtype": "Select",
"label": "Type of Transaction",
"options": "\nInward\nOutward\nMaintenance\nAsset Repair",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"fieldname": "naming_series",
@@ -244,7 +248,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-07-16 10:53:04.045605",
"modified": "2023-07-26 12:56:03.072224",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial and Batch Bundle",

View File

@@ -173,7 +173,8 @@
"fieldtype": "Link",
"label": "Batch No",
"options": "Batch",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"default": "0",
@@ -197,7 +198,8 @@
"label": "Serial / Batch Bundle",
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"fieldname": "current_serial_and_batch_bundle",
@@ -214,7 +216,7 @@
],
"istable": 1,
"links": [],
"modified": "2023-06-15 11:45:55.808942",
"modified": "2023-07-26 12:54:34.011915",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation Item",

View File

@@ -8,7 +8,7 @@ frappe.query_reports["Batch Item Expiry Status"] = {
"label": __("From Date"),
"fieldtype": "Date",
"width": "80",
"default": frappe.sys_defaults.year_start_date,
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1,
},
{

View File

@@ -16,7 +16,7 @@ frappe.query_reports["Batch-Wise Balance History"] = {
"label": __("From Date"),
"fieldtype": "Date",
"width": "80",
"default": frappe.sys_defaults.year_start_date,
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
"reqd": 1
},
{

View File

@@ -17,7 +17,7 @@ def execute(filters=None):
if not filters:
filters = {}
sle_count = frappe.db.count("Stock Ledger Entry", {"is_cancelled": 0})
sle_count = frappe.db.count("Stock Ledger Entry")
if sle_count > SLE_COUNT_LIMIT and not filters.get("item_code") and not filters.get("warehouse"):
frappe.throw(_("Please select either the Item or Warehouse filter to generate the report."))

View File

@@ -22,14 +22,14 @@ frappe.query_reports["Incorrect Serial No Valuation"] = {
fieldtype: 'Date',
fieldname: 'from_date',
reqd: 1,
default: frappe.defaults.get_user_default("year_start_date")
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
label: __('To Date'),
fieldtype: 'Date',
fieldname: 'to_date',
reqd: 1,
default: frappe.defaults.get_user_default("year_end_date")
default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2],
}
]
};

View File

@@ -7,7 +7,7 @@ frappe.query_reports["Itemwise Recommended Reorder Level"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.sys_defaults.year_start_date
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1],
},
{
"fieldname":"to_date",

View File

@@ -446,10 +446,9 @@ class StockBalanceReport(object):
{
"label": _("Valuation Rate"),
"fieldname": "val_rate",
"fieldtype": "Currency",
"fieldtype": "Float",
"width": 90,
"convertible": "rate",
"options": "currency",
},
{
"label": _("Reserved Stock"),

View File

@@ -196,7 +196,7 @@ def get_columns(filters):
{
"label": _("Avg Rate (Balance Stock)"),
"fieldname": "valuation_rate",
"fieldtype": "Currency",
"fieldtype": "Float",
"width": 180,
"options": "Company:company:default_currency",
"convertible": "rate",
@@ -204,7 +204,7 @@ def get_columns(filters):
{
"label": _("Valuation Rate"),
"fieldname": "in_out_rate",
"fieldtype": "Currency",
"fieldtype": "Float",
"width": 140,
"options": "Company:company:default_currency",
"convertible": "rate",

View File

@@ -358,6 +358,8 @@ def update_args_in_repost_item_valuation(
"current_index": index,
"total_reposting_count": len(args),
},
doctype=doc.doctype,
docname=doc.name,
)

View File

@@ -17,7 +17,7 @@
title = "Warehouse",
actual_qty = (frm.doc.doctype==="Sales Order"
? doc.projected_qty : doc.actual_qty);
if(flt(frm.doc.per_delivered) < 100
if(flt(frm.doc.per_delivered, 2) < 100
&& in_list(["Sales Order Item", "Delivery Note Item"], doc.doctype)) {
if(actual_qty != undefined) {
if(actual_qty >= doc.qty) {

View File

@@ -1063,6 +1063,7 @@ Get Items from Prescriptions,Holen Sie sich Artikel aus Verordnungen,
Get Items from Product Bundle,Artikel aus dem Produkt-Bundle übernehmen,
Get Suppliers,Holen Sie sich Lieferanten,
Get Suppliers By,Holen Sie sich Lieferanten durch,
Get Supplier Group Details,Werte aus Lieferantengruppe übernehmen,
Get Updates,Newsletter abonnieren,
Get customers from,Holen Sie Kunden von,
Get from Patient Encounter,Von der Patientenbegegnung erhalten,
@@ -4726,6 +4727,7 @@ Credit To,Gutschreiben auf,
Party Account Currency,Währung des Kontos der Partei,
Against Expense Account,Zu Aufwandskonto,
Inter Company Invoice Reference,Unternehmensübergreifende Rechnungsreferenz,
Internal Supplier,Interner Lieferant,
Is Internal Supplier,Ist interner Lieferant,
Start date of current invoice's period,Startdatum der laufenden Rechnungsperiode,
End date of current invoice's period,Schlußdatum der laufenden Eingangsrechnungsperiode,
@@ -5172,6 +5174,8 @@ Tracking,Verfolgung,
Ref SQ,Ref-SQ,
Inter Company Order Reference,Inter Company Bestellreferenz,
Supplier Part Number,Lieferanten-Artikelnummer,
Supplier Primary Contact,Hauptkontakt des Lieferanten,
Supplier Primary Address,Hauptadresse des Lieferanten,
Billed Amt,Rechnungsbetrag,
Warehouse and Reference,Lager und Referenz,
To be delivered to customer,Zur Auslieferung an den Kunden,
@@ -6773,7 +6777,7 @@ Accounts Manager,Buchhalter,
Allow Sales Invoice Creation Without Sales Order,Ermöglichen Sie die Erstellung von Kundenrechnungen ohne Auftrag,
Allow Sales Invoice Creation Without Delivery Note,Ermöglichen Sie die Erstellung einer Ausgangsrechnung ohne Lieferschein,
Default Price List,Standardpreisliste,
Primary Address and Contact Detail,Primäre Adresse und Kontaktdetails,
Primary Address and Contact,Hauptadresse und -kontakt,
"Select, to make the customer searchable with these fields","Wählen Sie, um den Kunden mit diesen Feldern durchsuchbar zu machen",
Customer Primary Contact,Hauptkontakt des Kunden,
"Reselect, if the chosen contact is edited after save","Wählen Sie erneut, wenn der ausgewählte Kontakt nach dem Speichern bearbeitet wird",
Can't render this file because it is too large.