mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-13 18:28:48 +00:00
Compare commits
172 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed50c3d896 | ||
|
|
85bb086e90 | ||
|
|
76b0f4fb25 | ||
|
|
167ff00c99 | ||
|
|
6e7fb2ea01 | ||
|
|
daf3aab6ae | ||
|
|
1c9a74f81c | ||
|
|
2eaffa119f | ||
|
|
6ad3461953 | ||
|
|
52db89f73f | ||
|
|
4f72635698 | ||
|
|
0e7f778b3f | ||
|
|
d6e9216a45 | ||
|
|
9cd60531d2 | ||
|
|
3763ad451b | ||
|
|
680fa3b8f3 | ||
|
|
0644c78c2e | ||
|
|
c2ad5d0fc7 | ||
|
|
6de74697c9 | ||
|
|
36197afa19 | ||
|
|
e762007e0e | ||
|
|
c462219dd7 | ||
|
|
0cd9330e44 | ||
|
|
faba523086 | ||
|
|
90913c66ae | ||
|
|
d47c25287d | ||
|
|
614d38d0e6 | ||
|
|
534b27afa5 | ||
|
|
faae734797 | ||
|
|
68c65866bf | ||
|
|
62db42cf2f | ||
|
|
106b83e9f9 | ||
|
|
9df6424a20 | ||
|
|
474ddbae0c | ||
|
|
9da2be2325 | ||
|
|
15e8fa3189 | ||
|
|
02d2ad6442 | ||
|
|
eafe33a176 | ||
|
|
bb2c21be44 | ||
|
|
d4519e5d3d | ||
|
|
1fceebd0a8 | ||
|
|
418d14ecc9 | ||
|
|
d3dde833f7 | ||
|
|
0e56c47a4c | ||
|
|
5f2725f61f | ||
|
|
b45d74c56e | ||
|
|
90e583db19 | ||
|
|
0d47eb1fa0 | ||
|
|
b060cdb4f5 | ||
|
|
09c96d6a83 | ||
|
|
8a3fdb4ec2 | ||
|
|
e0313bb27f | ||
|
|
04c5369792 | ||
|
|
a11d368465 | ||
|
|
fb126e0838 | ||
|
|
96e467670a | ||
|
|
105838091e | ||
|
|
90a0873044 | ||
|
|
965de96251 | ||
|
|
1a57f603fb | ||
|
|
36cb5b6589 | ||
|
|
dab6bb8e05 | ||
|
|
eebeb36b6c | ||
|
|
38c886db8b | ||
|
|
f4f1fdedee | ||
|
|
24ca7bb64f | ||
|
|
849f646bd2 | ||
|
|
e19c4c6452 | ||
|
|
e693ab76fa | ||
|
|
6b38654542 | ||
|
|
935b8cfcf7 | ||
|
|
639c75312d | ||
|
|
e1121d1c68 | ||
|
|
5224b6677d | ||
|
|
ad3d6a7c91 | ||
|
|
2d420ed661 | ||
|
|
4cc9061990 | ||
|
|
5266690cd8 | ||
|
|
373a17e3de | ||
|
|
2af2002431 | ||
|
|
5b4093069c | ||
|
|
53fef46f9a | ||
|
|
1070322695 | ||
|
|
3ea6278eb9 | ||
|
|
e822a74479 | ||
|
|
e36e5027d7 | ||
|
|
f97e29058c | ||
|
|
964f9275dc | ||
|
|
f3895e9dc9 | ||
|
|
3e8deeed07 | ||
|
|
84e91e0c7c | ||
|
|
83e9842dd3 | ||
|
|
3760ff8a7a | ||
|
|
ddecce400a | ||
|
|
6367bd9561 | ||
|
|
104569318e | ||
|
|
4a8465df90 | ||
|
|
78857cd798 | ||
|
|
2c9ee7ae14 | ||
|
|
4a5e0b181f | ||
|
|
35f826c499 | ||
|
|
b565d4f0a8 | ||
|
|
b964b122ed | ||
|
|
cee9f200ad | ||
|
|
214f15e700 | ||
|
|
0341941d3f | ||
|
|
8d1b599d5f | ||
|
|
4022fcb0d9 | ||
|
|
4f14651a42 | ||
|
|
497247d89a | ||
|
|
137d2d4044 | ||
|
|
37eaa07192 | ||
|
|
9d8cb2f57c | ||
|
|
eec327c02b | ||
|
|
093c94aa7c | ||
|
|
19a8ddef86 | ||
|
|
0fdd944418 | ||
|
|
f997393b0e | ||
|
|
0d02d7086c | ||
|
|
913bde0dfe | ||
|
|
21d33cf6b9 | ||
|
|
dc7ac3550e | ||
|
|
e8ba2b1576 | ||
|
|
d9c1ef0926 | ||
|
|
0061bde950 | ||
|
|
5a984de697 | ||
|
|
3603cdf457 | ||
|
|
a74b3e3602 | ||
|
|
8259a748f6 | ||
|
|
59c6e8f233 | ||
|
|
dfa35363b9 | ||
|
|
bab2e86c01 | ||
|
|
fc462d4526 | ||
|
|
5b0486ca26 | ||
|
|
dc4b236951 | ||
|
|
070190d07b | ||
|
|
03550066a7 | ||
|
|
b93cfc5d83 | ||
|
|
ad56177234 | ||
|
|
e22f93f1fb | ||
|
|
56dca02cab | ||
|
|
c7db277aa8 | ||
|
|
4ce4d345e7 | ||
|
|
f714888d48 | ||
|
|
0207b82f82 | ||
|
|
7f1dbeee8b | ||
|
|
a96fa55704 | ||
|
|
4488b27fe3 | ||
|
|
d862a742b0 | ||
|
|
cb0addc122 | ||
|
|
5de5a8bfd5 | ||
|
|
5291c66ea1 | ||
|
|
ed927a2147 | ||
|
|
2f4a9f283d | ||
|
|
79bfcbf424 | ||
|
|
c9d69d9629 | ||
|
|
6f12029477 | ||
|
|
7bb127de63 | ||
|
|
87de70f4fd | ||
|
|
782fecca46 | ||
|
|
8cd90de70b | ||
|
|
0b9a8ee67c | ||
|
|
652589f636 | ||
|
|
106f7ea112 | ||
|
|
db41b14317 | ||
|
|
609191c3a5 | ||
|
|
7489c9159d | ||
|
|
b48619078d | ||
|
|
523007588d | ||
|
|
184cabdcb0 | ||
|
|
f8ea431551 | ||
|
|
284e45011e |
4
.github/workflows/patch_faux.yml
vendored
4
.github/workflows/patch_faux.yml
vendored
@@ -1,7 +1,6 @@
|
||||
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
|
||||
|
||||
name: Skipped Patch Test
|
||||
permissions: none
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -12,6 +11,9 @@ on:
|
||||
- "**.html"
|
||||
- "**.csv"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Entire Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Tests are skipped for these files but github doesn't allow "passing" hence this is required.
|
||||
|
||||
name: Skipped Tests
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -11,6 +10,9 @@ on:
|
||||
- "**.md"
|
||||
- "**.html"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
25
CODEOWNERS
25
CODEOWNERS
@@ -3,22 +3,21 @@
|
||||
# These owners will be the default owners for everything in
|
||||
# the repo. Unless a later match takes precedence,
|
||||
|
||||
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/assets/ @khushi8112 @deepeshgarg007
|
||||
erpnext/regional @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/selling @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/support/ @deepeshgarg007
|
||||
pos*
|
||||
erpnext/accounts/ @ruthra-kumar
|
||||
erpnext/assets/ @khushi8112
|
||||
erpnext/regional @ruthra-kumar
|
||||
erpnext/selling @ruthra-kumar
|
||||
erpnext/support/ @ruthra-kumar
|
||||
|
||||
erpnext/buying/ @rohitwaghchaure
|
||||
erpnext/buying/ @rohitwaghchaure @mihir-kandoi
|
||||
erpnext/maintenance/ @rohitwaghchaure
|
||||
erpnext/manufacturing/ @rohitwaghchaure
|
||||
erpnext/manufacturing/ @rohitwaghchaure @mihir-kandoi
|
||||
erpnext/quality_management/ @rohitwaghchaure
|
||||
erpnext/stock/ @rohitwaghchaure
|
||||
erpnext/subcontracting @rohitwaghchaure
|
||||
erpnext/stock/ @rohitwaghchaure @mihir-kandoi
|
||||
erpnext/subcontracting @mihir-kandoi
|
||||
|
||||
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
|
||||
erpnext/patches/ @deepeshgarg007
|
||||
erpnext/controllers/ @ruthra-kumar @rohitwaghchaure @mihir-kandoi
|
||||
erpnext/patches/ @ruthra-kumar
|
||||
|
||||
.github/ @deepeshgarg007
|
||||
.github/ @ruthra-kumar
|
||||
pyproject.toml @akhilnarang
|
||||
|
||||
@@ -4,7 +4,7 @@ import inspect
|
||||
import frappe
|
||||
from frappe.utils.user import is_website_user
|
||||
|
||||
__version__ = "15.72.3"
|
||||
__version__ = "15.75.0"
|
||||
|
||||
|
||||
def get_default_company(user=None):
|
||||
|
||||
@@ -169,7 +169,7 @@ class Account(NestedSet):
|
||||
if par.root_type:
|
||||
self.root_type = par.root_type
|
||||
|
||||
if self.is_group:
|
||||
if cint(self.is_group):
|
||||
db_value = self.get_doc_before_save()
|
||||
if db_value:
|
||||
if self.report_type != db_value.report_type:
|
||||
@@ -212,7 +212,7 @@ class Account(NestedSet):
|
||||
if doc_before_save and not doc_before_save.parent_account:
|
||||
throw(_("Root cannot be edited."), RootNotEditable)
|
||||
|
||||
if not self.parent_account and not self.is_group:
|
||||
if not self.parent_account and not cint(self.is_group):
|
||||
throw(_("The root account {0} must be a group").format(frappe.bold(self.name)))
|
||||
|
||||
def validate_root_company_and_sync_account_to_children(self):
|
||||
@@ -261,7 +261,7 @@ class Account(NestedSet):
|
||||
|
||||
if self.check_gle_exists():
|
||||
throw(_("Account with existing transaction cannot be converted to ledger"))
|
||||
elif self.is_group:
|
||||
elif cint(self.is_group):
|
||||
if self.account_type and not self.flags.exclude_account_type_check:
|
||||
throw(_("Cannot covert to Group because Account Type is selected."))
|
||||
elif self.check_if_child_exists():
|
||||
@@ -304,7 +304,9 @@ class Account(NestedSet):
|
||||
self.account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
self.currency_explicitly_specified = False
|
||||
|
||||
gl_currency = frappe.db.get_value("GL Entry", {"account": self.name}, "account_currency")
|
||||
gl_currency = frappe.db.get_value(
|
||||
"GL Entry", {"account": self.name, "is_cancelled": 0}, "account_currency"
|
||||
)
|
||||
|
||||
if gl_currency and self.account_currency != gl_currency:
|
||||
if frappe.db.get_value("GL Entry", {"account": self.name}):
|
||||
|
||||
@@ -252,10 +252,6 @@ frappe.treeview_settings["Account"] = {
|
||||
root_company,
|
||||
]);
|
||||
} else {
|
||||
const node = treeview.tree.get_selected_node();
|
||||
if (node.is_root) {
|
||||
frappe.throw(__("Cannot create root account."));
|
||||
}
|
||||
treeview.new_node();
|
||||
}
|
||||
},
|
||||
@@ -274,8 +270,7 @@ frappe.treeview_settings["Account"] = {
|
||||
].treeview.page.fields_dict.root_company.get_value() ||
|
||||
frappe.flags.ignore_root_company_validation) &&
|
||||
node.expandable &&
|
||||
!node.hide_add &&
|
||||
!node.is_root
|
||||
!node.hide_add
|
||||
);
|
||||
},
|
||||
click: function () {
|
||||
|
||||
@@ -0,0 +1,817 @@
|
||||
{
|
||||
"country_code": "au",
|
||||
"name": "Australia - Chart of Accounts with Account Numbers",
|
||||
"tree": {
|
||||
"Assets": {
|
||||
"Current Assets": {
|
||||
"Cash On Hand": {
|
||||
"Cash On Hand": {
|
||||
"account_number": "11010",
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"account_number": "110",
|
||||
"is_group": 1
|
||||
},
|
||||
"Cash at Bank": {
|
||||
"Every Day Bank Account": {
|
||||
"account_number": "11510",
|
||||
"account_type": "Bank"
|
||||
},
|
||||
"Business Savings Account": {
|
||||
"account_number": "11520"
|
||||
},
|
||||
"Business Term Deposit": {
|
||||
"account_number": "11530"
|
||||
},
|
||||
"account_number": "115",
|
||||
"is_group": 1
|
||||
},
|
||||
"Trade Receivables": {
|
||||
"Trade Debtors": {
|
||||
"account_number": "12010",
|
||||
"account_type": "Receivable"
|
||||
},
|
||||
"Provision for Doubtful Debts": {
|
||||
"account_number": "12020"
|
||||
},
|
||||
"Sundry Debtors": {
|
||||
"account_number": "12030"
|
||||
},
|
||||
"Debtor Refund": {
|
||||
"account_number": "12040"
|
||||
},
|
||||
"account_number": "120",
|
||||
"is_group": 1
|
||||
},
|
||||
"Inventory": {
|
||||
"Stock On Hand": {
|
||||
"account_number": "13010",
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"WIP - Work In Progress - Manufacturing": {
|
||||
"account_number": "13020"
|
||||
},
|
||||
"account_number": "130",
|
||||
"is_group": 1
|
||||
},
|
||||
"Prepayments": {
|
||||
"Prepayments": {
|
||||
"account_number": "14010"
|
||||
},
|
||||
"Provisional Tax Paid": {
|
||||
"account_number": "14020"
|
||||
},
|
||||
"account_number": "140",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "11",
|
||||
"is_group": 1
|
||||
},
|
||||
"Non Current Assets": {
|
||||
"Plant & Equipment": {
|
||||
"Plant & Equipment": {
|
||||
"account_number": "16010",
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciation Plant & Equipment": {
|
||||
"account_number": "16020",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"account_number": "160",
|
||||
"is_group": 1
|
||||
},
|
||||
"Motor Vehicle": {
|
||||
"Motor Vehicle": {
|
||||
"account_number": "16110",
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciation Motor Vehicle": {
|
||||
"account_number": "16120",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"account_number": "161",
|
||||
"is_group": 1
|
||||
},
|
||||
"Office Equipment": {
|
||||
"Office Furniture & Equipment": {
|
||||
"account_number": "16210",
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciation Office Furniture & Equipment": {
|
||||
"account_number": "16220",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"account_number": "162",
|
||||
"is_group": 1
|
||||
},
|
||||
"Computer Equipment": {
|
||||
"Computer Equipment": {
|
||||
"account_number": "16310",
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciation Computer Equipment": {
|
||||
"account_number": "16320",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"account_number": "163",
|
||||
"is_group": 1
|
||||
},
|
||||
"Building": {
|
||||
"Buildings": {
|
||||
"account_number": "16410",
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciation Buildings": {
|
||||
"account_number": "16420",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"CWIP - Construction Work In Progress": {
|
||||
"account_number": "16430",
|
||||
"account_type": "Capital Work in Progress"
|
||||
},
|
||||
"Accumulated Depreciation - Others": {
|
||||
"account_number": "16440",
|
||||
"account_type": "Accumulated Depreciation"
|
||||
},
|
||||
"account_number": "164",
|
||||
"is_group": 1
|
||||
},
|
||||
"Related Party": {
|
||||
"Loan to Party 1": {
|
||||
"account_number": "17010"
|
||||
},
|
||||
"account_number": "170",
|
||||
"is_group": 1
|
||||
},
|
||||
"Investments & Unlisted Entities": {
|
||||
"Investment - Entity 1": {
|
||||
"account_number": "17510"
|
||||
},
|
||||
"account_number": "175",
|
||||
"is_group": 1
|
||||
},
|
||||
"Intagible Assets": {
|
||||
"Goodwill": {
|
||||
"account_number": "18010"
|
||||
},
|
||||
"Opening Balance Temporary ": {
|
||||
"account_number": "18090",
|
||||
"account_type": "Temporary"
|
||||
},
|
||||
"account_number": "180",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "16",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "1",
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Liabilities": {
|
||||
"Current Liabilities": {
|
||||
"Trade Payables - Current": {
|
||||
"Trade Creditors": {
|
||||
"account_number": "21010",
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Goods Received Not Invoiced": {
|
||||
"account_number": "21050",
|
||||
"account_type": "Stock Received But Not Billed"
|
||||
},
|
||||
"Service Received Not Invoiced": {
|
||||
"account_number": "21060"
|
||||
},
|
||||
"Asset Received Not Invoiced": {
|
||||
"account_number": "21070",
|
||||
"account_type": "Asset Received But Not Billed"
|
||||
},
|
||||
"account_number": "210",
|
||||
"is_group": 1
|
||||
},
|
||||
"Other Payables - Current": {
|
||||
"Accrued Expenses": {
|
||||
"account_number": "21510"
|
||||
},
|
||||
"Payroll - Wages Clearing": {
|
||||
"account_number": "21550"
|
||||
},
|
||||
"Payroll - Superannuation Deductions": {
|
||||
"account_number": "21555"
|
||||
},
|
||||
"Payroll - Misc Deductions": {
|
||||
"account_number": "21560"
|
||||
},
|
||||
"Payroll - Withholding Tax Payable": {
|
||||
"account_number": "21565"
|
||||
},
|
||||
"account_number": "215",
|
||||
"is_group": 1
|
||||
},
|
||||
"GST": {
|
||||
"GST Payments to ATO": {
|
||||
"account_number": "22030"
|
||||
},
|
||||
"Provision for PAYG Tax": {
|
||||
"account_number": "22040"
|
||||
},
|
||||
"account_number": "220",
|
||||
"account_type": "Tax",
|
||||
"is_group": 1
|
||||
},
|
||||
"Interest & Non Bearing Liabilities - Current": {
|
||||
"Credit Card - VISA": {
|
||||
"account_number": "22510"
|
||||
},
|
||||
"account_number": "225",
|
||||
"is_group": 1
|
||||
},
|
||||
"Bank Overdraft": {
|
||||
"Bank Overdraft Cash at Bank": {
|
||||
"account_number": "23010"
|
||||
},
|
||||
"account_number": "230",
|
||||
"is_group": 1
|
||||
},
|
||||
"Trade Finance": {
|
||||
"Trade Finance": {
|
||||
"account_number": "23510"
|
||||
},
|
||||
"account_number": "235",
|
||||
"is_group": 1
|
||||
},
|
||||
"Lease Liabilities": {
|
||||
"Finance Lease - Current": {
|
||||
"account_number": "24010"
|
||||
},
|
||||
"account_number": "240",
|
||||
"is_group": 1
|
||||
},
|
||||
"Provisions": {
|
||||
"Provision for Long Service Leave": {
|
||||
"account_number": "24510"
|
||||
},
|
||||
"Provision for Holiday Pay": {
|
||||
"account_number": "24520"
|
||||
},
|
||||
"account_number": "245",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "21",
|
||||
"is_group": 1
|
||||
},
|
||||
"Non Current Liabilities": {
|
||||
"Trade & Other Payables - Non Current": {
|
||||
"Loan Account - Party 1": {
|
||||
"account_number": "25010"
|
||||
},
|
||||
"account_number": "250",
|
||||
"is_group": 1
|
||||
},
|
||||
"Interest & Non Bearing Liabilities - Non Current": {
|
||||
"Non Current Liability - Director Loan": {
|
||||
"account_number": "25510"
|
||||
},
|
||||
"account_number": "255",
|
||||
"is_group": 1
|
||||
},
|
||||
"Bank Loans - Non Current": {
|
||||
"Bank Loan 1 - Non Current": {
|
||||
"account_number": "26010"
|
||||
},
|
||||
"account_number": "260",
|
||||
"is_group": 1
|
||||
},
|
||||
"Lease Liabilities - Non Current": {
|
||||
"Finance Lease - Non Current": {
|
||||
"account_number": "27010"
|
||||
},
|
||||
"account_number": "270",
|
||||
"is_group": 1
|
||||
},
|
||||
"Provisions - Non Current": {
|
||||
"Provision for Long Service Leave": {
|
||||
"account_number": "27510"
|
||||
},
|
||||
"Provision for Holiday Pay": {
|
||||
"account_number": "27520"
|
||||
},
|
||||
"account_number": "275",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "25",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "2",
|
||||
"root_type": "Liability"
|
||||
},
|
||||
"Equity": {
|
||||
"Equity": {
|
||||
"Owner's/Shareholder's Equity": {
|
||||
"Owner's/Shareholders Capital": {
|
||||
"account_number": "31010",
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"Owner's/Shareholders Drawings": {
|
||||
"account_number": "31020",
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"account_number": "310",
|
||||
"is_group": 1
|
||||
},
|
||||
"Earnings": {
|
||||
"Current Year Earnings": {
|
||||
"account_number": "35010",
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"Retained Earnings": {
|
||||
"account_number": "35020",
|
||||
"account_type": "Equity"
|
||||
},
|
||||
"account_number": "350",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "31",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "3",
|
||||
"root_type": "Equity"
|
||||
},
|
||||
"Revenue": {
|
||||
"Revenue": {
|
||||
"Sales Revenue": {
|
||||
"Sales Income": {
|
||||
"account_number": "41010",
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Freight Income": {
|
||||
"account_number": "41020",
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Other Income": {
|
||||
"account_number": "41030",
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Service Income": {
|
||||
"account_number": "41040",
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"account_number": "410",
|
||||
"is_group": 1
|
||||
},
|
||||
"Other Revenue": {
|
||||
"Commission Received": {
|
||||
"account_number": "42010"
|
||||
},
|
||||
"Discounts Received": {
|
||||
"account_number": "42020"
|
||||
},
|
||||
"Interest received": {
|
||||
"account_number": "42030"
|
||||
},
|
||||
"Profit/Loss on Sales of Assets": {
|
||||
"account_number": "42040"
|
||||
},
|
||||
"Rent Received": {
|
||||
"account_number": "42050"
|
||||
},
|
||||
"Sundry Income": {
|
||||
"account_number": "42060"
|
||||
},
|
||||
"account_number": "420",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "41",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "4",
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Cost of Goods": {
|
||||
"Cost of Goods": {
|
||||
"Cost of Goods Sold": {
|
||||
"Cost of Goods Sold": {
|
||||
"account_number": "51010",
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Freight Expenses (sales related)": {
|
||||
"account_number": "51020"
|
||||
},
|
||||
"Discounts Given": {
|
||||
"account_number": "51030"
|
||||
},
|
||||
"Subcontracting Charges": {
|
||||
"account_number": "51040"
|
||||
},
|
||||
"account_number": "510",
|
||||
"is_group": 1
|
||||
},
|
||||
"Other COGS": {
|
||||
"Purchases - Miscellaneous": {
|
||||
"account_number": "52010"
|
||||
},
|
||||
"Duty & Customs Fees": {
|
||||
"account_number": "52020",
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"Freight Inwards": {
|
||||
"account_number": "52030",
|
||||
"account_type": "Chargeable"
|
||||
},
|
||||
"Stock Adjustment": {
|
||||
"account_number": "52040",
|
||||
"account_type": "Stock Adjustment"
|
||||
},
|
||||
"Stock Wirte Off": {
|
||||
"account_number": "52050",
|
||||
"account_type": "Stock Adjustment"
|
||||
},
|
||||
"Stock Valuation Expenses": {
|
||||
"account_number": "52060",
|
||||
"account_type": "Expenses Included In Valuation"
|
||||
},
|
||||
"Asset Valuation Expenses": {
|
||||
"account_number": "52070",
|
||||
"account_type": "Expenses Included In Asset Valuation"
|
||||
},
|
||||
"account_number": "520",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "51",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "5",
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Expenses": {
|
||||
"Fixed Expenses": {
|
||||
"Payroll & Related Expenses": {
|
||||
"Salaries & Wages": {
|
||||
"account_number": "61010"
|
||||
},
|
||||
"Superannuation": {
|
||||
"account_number": "61015"
|
||||
},
|
||||
"Staff Amenities - GST Paid": {
|
||||
"account_number": "61020"
|
||||
},
|
||||
"Staff Amenities - GST Free": {
|
||||
"account_number": "61025"
|
||||
},
|
||||
"Staff Recruitment": {
|
||||
"account_number": "61030"
|
||||
},
|
||||
"Staff Training": {
|
||||
"account_number": "61035"
|
||||
},
|
||||
"Fringe Benefits Tax": {
|
||||
"account_number": "61040"
|
||||
},
|
||||
"Payroll Tax": {
|
||||
"account_number": "61045"
|
||||
},
|
||||
"Workers Compensation": {
|
||||
"account_number": "61050"
|
||||
},
|
||||
"Long Service Leave": {
|
||||
"account_number": "61060"
|
||||
},
|
||||
"Mileage Reimbursement": {
|
||||
"account_number": "61070"
|
||||
},
|
||||
"Overtime": {
|
||||
"account_number": "61080"
|
||||
},
|
||||
"Worksafe Insurance": {
|
||||
"account_number": "61090"
|
||||
},
|
||||
"account_number": "610",
|
||||
"is_group": 1
|
||||
},
|
||||
"Depreciation Expenses": {
|
||||
"Depreciation - Plant & Equipment": {
|
||||
"account_number": "62010",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Depreciation - Motor Vehicle": {
|
||||
"account_number": "62020",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Depreciation - Office Equipment": {
|
||||
"account_number": "62030",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Depreciation - Computer Equipment": {
|
||||
"account_number": "62040",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Depreciation - Building": {
|
||||
"account_number": "62050",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Depreciation - Others": {
|
||||
"account_number": "62510",
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"account_number": "620",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "61",
|
||||
"is_group": 1
|
||||
},
|
||||
"Accrued Expenses": {
|
||||
"Accrued Expenses": {
|
||||
"Accrued Expenses - Salaries & Wages": {
|
||||
"account_number": "63010"
|
||||
},
|
||||
"Accrued Expenses - Interest": {
|
||||
"account_number": "63020"
|
||||
},
|
||||
"account_number": "630",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "63",
|
||||
"is_group": 1
|
||||
},
|
||||
"Operating Expenses": {
|
||||
"General and Administrative Expenses": {
|
||||
"Low Value Assets less than $300": {
|
||||
"account_number": "64010"
|
||||
},
|
||||
"Office Supplies": {
|
||||
"account_number": "64020"
|
||||
},
|
||||
"Postage & Courier": {
|
||||
"account_number": "64025"
|
||||
},
|
||||
"Printing & Stationery": {
|
||||
"account_number": "64030"
|
||||
},
|
||||
"Registration Fees / Filing Fees": {
|
||||
"account_number": "64040"
|
||||
},
|
||||
"Travel & Accommodation - Local": {
|
||||
"account_number": "64050"
|
||||
},
|
||||
"Travel & Accommodation - Overseas": {
|
||||
"account_number": "64060"
|
||||
},
|
||||
"Relocation Costs": {
|
||||
"account_number": "64070"
|
||||
},
|
||||
"Hire Charges": {
|
||||
"account_number": "64080"
|
||||
},
|
||||
"Repairs & Maintenance": {
|
||||
"account_number": "64210"
|
||||
},
|
||||
"Cleaning Expenses": {
|
||||
"account_number": "64215"
|
||||
},
|
||||
"Uniforms": {
|
||||
"account_number": "64220"
|
||||
},
|
||||
"Security": {
|
||||
"account_number": "64225"
|
||||
},
|
||||
"Subscriptions & Licences": {
|
||||
"account_number": "64510"
|
||||
},
|
||||
"Software Expenses": {
|
||||
"account_number": "64515"
|
||||
},
|
||||
"Marketing Expenses": {
|
||||
"account_number": "64520"
|
||||
},
|
||||
"Advertising Expenses": {
|
||||
"account_number": "64525"
|
||||
},
|
||||
"Website Hosting & Domain Expenses": {
|
||||
"account_number": "64530"
|
||||
},
|
||||
"Computer Repairs / Supplies": {
|
||||
"account_number": "64540"
|
||||
},
|
||||
"Conferences": {
|
||||
"account_number": "64550"
|
||||
},
|
||||
"Consultancy /Contract Services": {
|
||||
"account_number": "64560"
|
||||
},
|
||||
"Training Services": {
|
||||
"account_number": "64570"
|
||||
},
|
||||
"Workshop Supplies": {
|
||||
"account_number": "64580"
|
||||
},
|
||||
"Consumables": {
|
||||
"account_number": "64585"
|
||||
},
|
||||
"Entertainment Expenses - Deductible": {
|
||||
"account_number": "64810"
|
||||
},
|
||||
"Entertainment Expenses - Non Deductible": {
|
||||
"account_number": "64820"
|
||||
},
|
||||
"Amortisation Of Goodwill": {
|
||||
"account_number": "64910"
|
||||
},
|
||||
"General / Miscellaneous Expenses": {
|
||||
"account_number": "64915",
|
||||
"account_type": "Chargeable"
|
||||
},
|
||||
"Donations": {
|
||||
"account_number": "64920"
|
||||
},
|
||||
"Client Gifts": {
|
||||
"account_number": "64930"
|
||||
},
|
||||
"Employee Gifts": {
|
||||
"account_number": "64935"
|
||||
},
|
||||
"account_number": "640",
|
||||
"is_group": 1
|
||||
},
|
||||
"Occupancy Expenses": {
|
||||
"Rental Expenses": {
|
||||
"account_number": "65010"
|
||||
},
|
||||
"Property Insurance": {
|
||||
"account_number": "65020"
|
||||
},
|
||||
"Electricity Expenses": {
|
||||
"account_number": "65030"
|
||||
},
|
||||
"Water Rates": {
|
||||
"account_number": "65040"
|
||||
},
|
||||
"Gas Expenses": {
|
||||
"account_number": "65050"
|
||||
},
|
||||
"Property Taxes": {
|
||||
"account_number": "65060"
|
||||
},
|
||||
"Rates": {
|
||||
"account_number": "65070"
|
||||
},
|
||||
"account_number": "650",
|
||||
"is_group": 1
|
||||
},
|
||||
"Communication & Vehicle Expenses": {
|
||||
"Internet Expenses": {
|
||||
"account_number": "66010"
|
||||
},
|
||||
"Mobile Telephone": {
|
||||
"account_number": "66020"
|
||||
},
|
||||
"Telephone Expenses": {
|
||||
"account_number": "66030"
|
||||
},
|
||||
"Motor Vehicle - Fuel Expenses": {
|
||||
"account_number": "66040"
|
||||
},
|
||||
"Motor Vehicle - Parking & Tolls": {
|
||||
"account_number": "66050"
|
||||
},
|
||||
"Motor Vehicle - Registration & Insurance": {
|
||||
"account_number": "66060"
|
||||
},
|
||||
"Motor Vehicle - Service & Repairs": {
|
||||
"account_number": "66070"
|
||||
},
|
||||
"Taxi": {
|
||||
"account_number": "66080"
|
||||
},
|
||||
"account_number": "660",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "64",
|
||||
"is_group": 1
|
||||
},
|
||||
"Non-Operating Expenses": {
|
||||
"Finance Costs": {
|
||||
"Interest - Bank Loans": {
|
||||
"account_number": "67010"
|
||||
},
|
||||
"Interest - Finance Leases": {
|
||||
"account_number": "67020"
|
||||
},
|
||||
"Interest - Other Loans": {
|
||||
"account_number": "67025"
|
||||
},
|
||||
"Insurance": {
|
||||
"account_number": "67030"
|
||||
},
|
||||
"Bank Charges": {
|
||||
"account_number": "67050"
|
||||
},
|
||||
"Rounding off": {
|
||||
"account_number": "67055",
|
||||
"account_type": "Round Off"
|
||||
},
|
||||
"Audit Fees": {
|
||||
"account_number": "67060"
|
||||
},
|
||||
"Accounting Fees": {
|
||||
"account_number": "67070"
|
||||
},
|
||||
"Legal Fees": {
|
||||
"account_number": "67080"
|
||||
},
|
||||
"Management Fees": {
|
||||
"account_number": "67090"
|
||||
},
|
||||
"account_number": "670",
|
||||
"is_group": 1
|
||||
},
|
||||
"Other Costs": {
|
||||
"Doubtful Debts": {
|
||||
"account_number": "67510"
|
||||
},
|
||||
"Fines": {
|
||||
"account_number": "67520"
|
||||
},
|
||||
"Debt Collection": {
|
||||
"account_number": "67530"
|
||||
},
|
||||
"Bad Debts": {
|
||||
"account_number": "67540"
|
||||
},
|
||||
"account_number": "675",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "67",
|
||||
"is_group": 1
|
||||
},
|
||||
"Variable Expenses": {
|
||||
"Variable Expenses": {
|
||||
"Bonus & Commissions Paid": {
|
||||
"account_number": "68010"
|
||||
},
|
||||
"Bonus & Commissions To be Paid": {
|
||||
"account_number": "68020"
|
||||
},
|
||||
"Warranty Claims": {
|
||||
"account_number": "68030"
|
||||
},
|
||||
"account_number": "680",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "68",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "6",
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Other Income": {
|
||||
"Other Income": {
|
||||
"Interest Income": {
|
||||
"Interest Income": {
|
||||
"account_number": "71010"
|
||||
},
|
||||
"account_number": "710",
|
||||
"is_group": 1
|
||||
},
|
||||
"Asset Disposal Income": {
|
||||
"Gain on Asset Disposal": {
|
||||
"account_number": "73010"
|
||||
},
|
||||
"account_number": "730",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "71",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "7",
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Other Expenses": {
|
||||
"Other Expenses": {
|
||||
"Income Tax Expenses": {
|
||||
"Income Tax Expenses": {
|
||||
"account_number": "81010"
|
||||
},
|
||||
"account_number": "810",
|
||||
"is_group": 1
|
||||
},
|
||||
"Foreign Exchange Gain/Loss": {
|
||||
"Exchange Loss/Gain - Realized": {
|
||||
"account_number": "82010"
|
||||
},
|
||||
"account_number": "820",
|
||||
"is_group": 1
|
||||
},
|
||||
"Asset Disposal Expenses": {
|
||||
"Loss on Asset Disposal": {
|
||||
"account_number": "83010"
|
||||
},
|
||||
"account_number": "830",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "81",
|
||||
"is_group": 1
|
||||
},
|
||||
"account_number": "8",
|
||||
"root_type": "Expense"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ def get():
|
||||
_("Bank Accounts"): {"account_type": "Bank", "is_group": 1},
|
||||
_("Cash In Hand"): {_("Cash"): {"account_type": "Cash"}, "account_type": "Cash"},
|
||||
_("Loans and Advances (Assets)"): {
|
||||
_("Employee Advances"): {},
|
||||
_("Employee Advances"): {"account_type": "Payable"},
|
||||
},
|
||||
_("Securities and Deposits"): {_("Earnest Money"): {}},
|
||||
_("Stock Assets"): {
|
||||
|
||||
@@ -20,7 +20,7 @@ def get():
|
||||
"account_number": "1100",
|
||||
},
|
||||
_("Loans and Advances (Assets)"): {
|
||||
_("Employee Advances"): {"account_number": "1610"},
|
||||
_("Employee Advances"): {"account_number": "1610", "account_type": "Payable"},
|
||||
"account_number": "1600",
|
||||
},
|
||||
_("Securities and Deposits"): {
|
||||
|
||||
@@ -111,17 +111,15 @@ class AccountingDimension(Document):
|
||||
def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||
if not doclist:
|
||||
doclist = get_doctypes_with_dimensions()
|
||||
|
||||
doc_count = len(get_accounting_dimensions())
|
||||
count = 0
|
||||
repostable_doctypes = get_allowed_types_from_settings()
|
||||
repostable_doctypes = get_allowed_types_from_settings(child_doc=True)
|
||||
|
||||
for doctype in doclist:
|
||||
if (doc_count + 1) % 2 == 0:
|
||||
insert_after_field = "dimension_col_break"
|
||||
else:
|
||||
insert_after_field = "accounting_dimensions_section"
|
||||
|
||||
df = {
|
||||
"fieldname": doc.fieldname,
|
||||
"label": doc.label,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.accounts.utils import update_voucher_outstanding
|
||||
from erpnext.accounts.utils import get_advance_payment_doctypes, update_voucher_outstanding
|
||||
|
||||
|
||||
class AdvancePaymentLedgerEntry(Document):
|
||||
@@ -29,7 +29,7 @@ class AdvancePaymentLedgerEntry(Document):
|
||||
|
||||
def on_update(self):
|
||||
if (
|
||||
self.against_voucher_type in ["Purchase Order", "Sales Order"]
|
||||
self.against_voucher_type in get_advance_payment_doctypes()
|
||||
and self.flags.update_outstanding == "Yes"
|
||||
and not frappe.flags.is_reverse_depr_entry
|
||||
):
|
||||
|
||||
@@ -109,6 +109,7 @@ class BankAccount(Document):
|
||||
"party_type": self.party_type,
|
||||
"party": self.party,
|
||||
"is_company_account": self.is_company_account,
|
||||
"company": self.company,
|
||||
"is_default": 1,
|
||||
"disabled": 0,
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.query_builder.custom import ConstantColumn
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import cint, flt
|
||||
from frappe.utils import cint, create_batch, flt
|
||||
|
||||
from erpnext import get_default_cost_center
|
||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
|
||||
@@ -377,16 +377,17 @@ def auto_reconcile_vouchers(
|
||||
bank_transactions = get_bank_transactions(bank_account)
|
||||
|
||||
if len(bank_transactions) > 10:
|
||||
frappe.enqueue(
|
||||
method="erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.start_auto_reconcile",
|
||||
queue="long",
|
||||
bank_transactions=bank_transactions,
|
||||
from_date=from_date,
|
||||
to_date=to_date,
|
||||
filter_by_reference_date=filter_by_reference_date,
|
||||
from_reference_date=from_reference_date,
|
||||
to_reference_date=to_reference_date,
|
||||
)
|
||||
for bank_transaction_batch in create_batch(bank_transactions, 1000):
|
||||
frappe.enqueue(
|
||||
method="erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.start_auto_reconcile",
|
||||
queue="long",
|
||||
bank_transactions=bank_transaction_batch,
|
||||
from_date=from_date,
|
||||
to_date=to_date,
|
||||
filter_by_reference_date=filter_by_reference_date,
|
||||
from_reference_date=from_reference_date,
|
||||
to_reference_date=to_reference_date,
|
||||
)
|
||||
frappe.msgprint(_("Auto Reconciliation has started in the background"))
|
||||
else:
|
||||
start_auto_reconcile(
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.tests.utils import change_settings
|
||||
from frappe.utils import add_days, today
|
||||
|
||||
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||
@@ -190,6 +192,31 @@ class TestCostCenterAllocation(unittest.TestCase):
|
||||
coa2.cancel()
|
||||
jv.cancel()
|
||||
|
||||
@change_settings("System Settings", {"rounding_method": "Commercial Rounding"})
|
||||
def test_debit_credit_on_cost_center_allocation_for_commercial_rounding(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
cca = create_cost_center_allocation(
|
||||
"_Test Company",
|
||||
"Main Cost Center 1 - _TC",
|
||||
{"Sub Cost Center 2 - _TC": 50, "Sub Cost Center 3 - _TC": 50},
|
||||
)
|
||||
|
||||
si = create_sales_invoice(rate=145.65, cost_center="Main Cost Center 1 - _TC")
|
||||
|
||||
gl_entry = frappe.qb.DocType("GL Entry")
|
||||
gl_entries = (
|
||||
frappe.qb.from_(gl_entry)
|
||||
.select(Sum(gl_entry.credit).as_("cr"), Sum(gl_entry.debit).as_("dr"))
|
||||
.where(gl_entry.voucher_type == "Sales Invoice")
|
||||
.where(gl_entry.voucher_no == si.name)
|
||||
).run(as_dict=1)
|
||||
|
||||
self.assertEqual(gl_entries[0].cr, gl_entries[0].dr)
|
||||
|
||||
si.cancel()
|
||||
cca.cancel()
|
||||
|
||||
|
||||
def create_cost_center_allocation(
|
||||
company,
|
||||
|
||||
@@ -552,6 +552,8 @@ class JournalEntry(AccountsController):
|
||||
elif (
|
||||
d.party_type
|
||||
and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
|
||||
and d.party_type
|
||||
!= "Employee" # making an excpetion for employee since they can be both payable and receivable
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row {0}: Account {1} and Party Type {2} have different account types").format(
|
||||
|
||||
@@ -8,4 +8,14 @@ frappe.ui.form.on("Payment Gateway Account", {
|
||||
frm.set_df_property("payment_gateway", "read_only", 1);
|
||||
}
|
||||
},
|
||||
|
||||
setup(frm) {
|
||||
frm.set_query("payment_account", function () {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"field_order": [
|
||||
"payment_gateway",
|
||||
"payment_channel",
|
||||
"company",
|
||||
"is_default",
|
||||
"column_break_4",
|
||||
"payment_account",
|
||||
@@ -70,11 +71,21 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Payment Channel",
|
||||
"options": "\nEmail\nPhone"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"print_hide": 1,
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-20 13:30:27.722852",
|
||||
"modified": "2025-07-14 16:49:55.210352",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Gateway Account",
|
||||
@@ -95,4 +106,4 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ class PaymentGatewayAccount(Document):
|
||||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
company: DF.Link
|
||||
currency: DF.ReadOnly | None
|
||||
is_default: DF.Check
|
||||
message: DF.SmallText | None
|
||||
@@ -24,7 +25,8 @@ class PaymentGatewayAccount(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def autoname(self):
|
||||
self.name = self.payment_gateway + " - " + self.currency
|
||||
abbr = frappe.db.get_value("Company", self.company, "abbr")
|
||||
self.name = self.payment_gateway + " - " + self.currency + " - " + abbr
|
||||
|
||||
def validate(self):
|
||||
self.currency = frappe.get_cached_value("Account", self.payment_account, "account_currency")
|
||||
@@ -34,13 +36,15 @@ class PaymentGatewayAccount(Document):
|
||||
|
||||
def update_default_payment_gateway(self):
|
||||
if self.is_default:
|
||||
frappe.db.sql(
|
||||
"""update `tabPayment Gateway Account` set is_default = 0
|
||||
where is_default = 1 """
|
||||
frappe.db.set_value(
|
||||
"Payment Gateway Account",
|
||||
{"is_default": 1, "name": ["!=", self.name], "company": self.company},
|
||||
"is_default",
|
||||
0,
|
||||
)
|
||||
|
||||
def set_as_default_if_not_set(self):
|
||||
if not frappe.db.get_value(
|
||||
"Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"
|
||||
if not frappe.db.exists(
|
||||
"Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name), "company": self.company}
|
||||
):
|
||||
self.is_default = 1
|
||||
|
||||
@@ -9,6 +9,14 @@ frappe.ui.form.on("Payment Request", {
|
||||
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("payment_gateway_account", function () {
|
||||
return {
|
||||
filters: {
|
||||
company: frm.doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -539,7 +539,9 @@ def make_payment_request(**args):
|
||||
if args.dt not in ALLOWED_DOCTYPES_FOR_PAYMENT_REQUEST:
|
||||
frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt)))
|
||||
|
||||
ref_doc = frappe.get_doc(args.dt, args.dn)
|
||||
ref_doc = args.ref_doc or frappe.get_doc(args.dt, args.dn)
|
||||
if not args.get("company"):
|
||||
args.company = ref_doc.company
|
||||
gateway_account = get_gateway_details(args) or frappe._dict()
|
||||
|
||||
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
|
||||
@@ -782,7 +784,7 @@ def get_gateway_details(args): # nosemgrep
|
||||
"""
|
||||
Return gateway and payment account of default payment gateway
|
||||
"""
|
||||
gateway_account = args.get("payment_gateway_account", {"is_default": 1})
|
||||
gateway_account = args.get("payment_gateway_account", {"is_default": 1, "company": args.company})
|
||||
if gateway_account:
|
||||
return get_payment_gateway_account(gateway_account)
|
||||
|
||||
|
||||
@@ -28,12 +28,14 @@ payment_method = [
|
||||
"payment_gateway": "_Test Gateway",
|
||||
"payment_account": "_Test Bank - _TC",
|
||||
"currency": "INR",
|
||||
"company": "_Test Company",
|
||||
},
|
||||
{
|
||||
"doctype": "Payment Gateway Account",
|
||||
"payment_gateway": "_Test Gateway",
|
||||
"payment_account": "_Test Bank USD - _TC",
|
||||
"currency": "USD",
|
||||
"company": "_Test Company",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -46,7 +48,11 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
for method in payment_method:
|
||||
if not frappe.db.get_value(
|
||||
"Payment Gateway Account",
|
||||
{"payment_gateway": method["payment_gateway"], "currency": method["currency"]},
|
||||
{
|
||||
"payment_gateway": method["payment_gateway"],
|
||||
"currency": method["currency"],
|
||||
"company": method["company"],
|
||||
},
|
||||
"name",
|
||||
):
|
||||
frappe.get_doc(method).insert(ignore_permissions=True)
|
||||
@@ -60,7 +66,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dt="Sales Order",
|
||||
dn=so_inr.name,
|
||||
recipient_id="saurabh@erpnext.com",
|
||||
payment_gateway_account="_Test Gateway - INR",
|
||||
payment_gateway_account="_Test Gateway - INR - _TC",
|
||||
)
|
||||
|
||||
self.assertEqual(pr.reference_doctype, "Sales Order")
|
||||
@@ -74,7 +80,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dt="Sales Invoice",
|
||||
dn=si_usd.name,
|
||||
recipient_id="saurabh@erpnext.com",
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
)
|
||||
|
||||
self.assertEqual(pr.reference_doctype, "Sales Invoice")
|
||||
@@ -95,7 +101,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
party="_Test Supplier USD",
|
||||
recipient_id="user@example.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
submit_doc=1,
|
||||
return_doc=1,
|
||||
)
|
||||
@@ -119,7 +125,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dn=purchase_invoice.name,
|
||||
recipient_id="user@example.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
return_doc=1,
|
||||
)
|
||||
|
||||
@@ -138,7 +144,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dn=purchase_invoice.name,
|
||||
recipient_id="user@example.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
return_doc=1,
|
||||
)
|
||||
|
||||
@@ -162,7 +168,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dn=so_inr.name,
|
||||
recipient_id="saurabh@erpnext.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - INR",
|
||||
payment_gateway_account="_Test Gateway - INR - _TC",
|
||||
submit_doc=1,
|
||||
return_doc=1,
|
||||
)
|
||||
@@ -184,7 +190,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dn=si_usd.name,
|
||||
recipient_id="saurabh@erpnext.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
submit_doc=1,
|
||||
return_doc=1,
|
||||
)
|
||||
@@ -228,7 +234,7 @@ class TestPaymentRequest(FrappeTestCase):
|
||||
dn=si_usd.name,
|
||||
recipient_id="saurabh@erpnext.com",
|
||||
mute_email=1,
|
||||
payment_gateway_account="_Test Gateway - USD",
|
||||
payment_gateway_account="_Test Gateway - USD - _TC",
|
||||
submit_doc=1,
|
||||
return_doc=1,
|
||||
)
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
@@ -1573,7 +1574,7 @@
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-07-17 16:51:40.886083",
|
||||
"modified": "2025-08-04 22:22:31.471752",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -78,18 +78,18 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.enable_auto_email == 0 && doc.report == 'General Ledger');",
|
||||
"depends_on": "eval:(!doc.enable_auto_email && doc.report == 'General Ledger');",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
"mandatory_depends_on": "eval:(!doc.enable_auto_email && doc.report == \"General Ledger\") "
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.enable_auto_email == 0 && doc.report == 'General Ledger');",
|
||||
"depends_on": "eval:(!doc.enable_auto_email && doc.report == 'General Ledger');",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
"mandatory_depends_on": "eval:(!doc.enable_auto_email && doc.report == \"General Ledger\") "
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
@@ -330,7 +330,8 @@
|
||||
"depends_on": "eval:(doc.report == 'Accounts Receivable');",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date"
|
||||
"label": "Posting Date",
|
||||
"mandatory_depends_on": "eval:(doc.report == 'Accounts Receivable');"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: (doc.report == 'Accounts Receivable');",
|
||||
@@ -400,7 +401,7 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2025-07-08 16:52:12.602384",
|
||||
"modified": "2025-08-04 18:21:12.603623",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
|
||||
@@ -550,7 +550,7 @@ def send_auto_email():
|
||||
selected = frappe.get_list(
|
||||
"Process Statement Of Accounts",
|
||||
filters={"enable_auto_email": 1},
|
||||
or_filters={"to_date": format_date(today()), "posting_date": format_date(today())},
|
||||
or_filters={"to_date": today(), "posting_date": today()},
|
||||
)
|
||||
for entry in selected:
|
||||
send_emails(entry.name, from_scheduler=True)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils import create_batch, getdate
|
||||
|
||||
from erpnext.accounts.doctype.subscription.subscription import DateTimeLikeObject, process_all
|
||||
|
||||
@@ -23,7 +23,23 @@ class ProcessSubscription(Document):
|
||||
# end: auto-generated types
|
||||
|
||||
def on_submit(self):
|
||||
process_all(subscription=self.subscription, posting_date=self.posting_date)
|
||||
self.process_all_subscription()
|
||||
|
||||
def process_all_subscription(self):
|
||||
filters = {"status": ("!=", "Cancelled")}
|
||||
|
||||
if self.subscription:
|
||||
filters["name"] = self.subscription
|
||||
|
||||
subscriptions = frappe.get_all("Subscription", filters, pluck="name")
|
||||
|
||||
for subscription in create_batch(subscriptions, 500):
|
||||
frappe.enqueue(
|
||||
method="erpnext.accounts.doctype.subscription.subscription.process_all",
|
||||
queue="long",
|
||||
subscription=subscription,
|
||||
posting_date=self.posting_date,
|
||||
)
|
||||
|
||||
|
||||
def create_subscription_process(
|
||||
|
||||
@@ -35,7 +35,10 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
this.frm.set_query("expense_account", "items", function () {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.get_expense_account",
|
||||
filters: { company: doc.company },
|
||||
filters: {
|
||||
company: doc.company,
|
||||
disabled: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
@@ -1650,7 +1651,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-04-09 16:49:22.175081",
|
||||
"modified": "2025-08-04 19:19:11.380664",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -5,6 +5,7 @@ import inspect
|
||||
|
||||
import frappe
|
||||
from frappe import _, qb
|
||||
from frappe.desk.form.linked_with import get_child_tables_of_doctypes
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.data import comma_and
|
||||
|
||||
@@ -177,7 +178,7 @@ def start_repost(account_repost_doc=str) -> None:
|
||||
if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
if not repost_doc.delete_cancelled_entries:
|
||||
doc.docstatus = 2
|
||||
doc.make_gl_entries_on_cancel()
|
||||
doc.make_gl_entries_on_cancel(from_repost=True)
|
||||
|
||||
doc.docstatus = 1
|
||||
if doc.doctype == "Sales Invoice":
|
||||
@@ -189,7 +190,7 @@ def start_repost(account_repost_doc=str) -> None:
|
||||
elif doc.doctype == "Purchase Receipt":
|
||||
if not repost_doc.delete_cancelled_entries:
|
||||
doc.docstatus = 2
|
||||
doc.make_gl_entries_on_cancel()
|
||||
doc.make_gl_entries_on_cancel(from_repost=True)
|
||||
|
||||
doc.docstatus = 1
|
||||
doc.make_gl_entries(from_repost=True)
|
||||
@@ -208,13 +209,29 @@ def start_repost(account_repost_doc=str) -> None:
|
||||
doc.make_gl_entries()
|
||||
|
||||
|
||||
def get_allowed_types_from_settings():
|
||||
return [
|
||||
def get_allowed_types_from_settings(child_doc: bool = False):
|
||||
repost_docs = [
|
||||
x.document_type
|
||||
for x in frappe.db.get_all(
|
||||
"Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"]
|
||||
)
|
||||
]
|
||||
result = repost_docs
|
||||
|
||||
if repost_docs and child_doc:
|
||||
result.extend(get_child_docs(repost_docs))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_child_docs(doc: list) -> list:
|
||||
child_doc = []
|
||||
doc = get_child_tables_of_doctypes(doc)
|
||||
for child_list in doc.values():
|
||||
for child in child_list:
|
||||
if child.get("child_table"):
|
||||
child_doc.append(child["child_table"])
|
||||
return child_doc
|
||||
|
||||
|
||||
def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
)
|
||||
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import get_child_docs
|
||||
|
||||
|
||||
class RepostAccountingLedgerSettings(Document):
|
||||
# begin: auto-generated types
|
||||
@@ -17,6 +22,24 @@ class RepostAccountingLedgerSettings(Document):
|
||||
from erpnext.accounts.doctype.repost_allowed_types.repost_allowed_types import RepostAllowedTypes
|
||||
|
||||
allowed_types: DF.Table[RepostAllowedTypes]
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
# end: auto-generated types
|
||||
def validate(self):
|
||||
self.update_property_for_accounting_dimension()
|
||||
|
||||
def update_property_for_accounting_dimension(self):
|
||||
doctypes = [entry.document_type for entry in self.allowed_types if entry.allowed]
|
||||
if not doctypes:
|
||||
return
|
||||
doctypes += get_child_docs(doctypes)
|
||||
|
||||
set_allow_on_submit_for_dimension_fields(doctypes)
|
||||
|
||||
|
||||
def set_allow_on_submit_for_dimension_fields(doctypes):
|
||||
for dt in doctypes:
|
||||
meta = frappe.get_meta(dt)
|
||||
for dimension in get_accounting_dimensions():
|
||||
df = meta.get_field(dimension)
|
||||
if df and not df.allow_on_submit:
|
||||
frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1)
|
||||
|
||||
@@ -379,6 +379,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"hide_days": 1,
|
||||
@@ -2189,7 +2190,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2025-06-26 14:06:56.773552",
|
||||
"modified": "2025-08-04 19:20:28.732039",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -1206,7 +1206,6 @@ class SalesInvoice(SellingController):
|
||||
|
||||
self.make_exchange_gain_loss_journal()
|
||||
elif self.docstatus == 2:
|
||||
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
||||
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
|
||||
if update_outstanding == "No":
|
||||
@@ -1370,8 +1369,9 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
asset.db_set("disposal_date", None)
|
||||
add_asset_activity(asset.name, _("Asset returned"))
|
||||
asset_status = asset.get_status()
|
||||
|
||||
if asset.calculate_depreciation:
|
||||
if asset.calculate_depreciation and not asset_status == "Fully Depreciated":
|
||||
posting_date = (
|
||||
frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
|
||||
if self.is_return
|
||||
|
||||
@@ -8,7 +8,7 @@ import frappe
|
||||
from frappe import qb
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, flt, format_date, getdate, nowdate, today
|
||||
from frappe.utils import add_days, cint, flt, format_date, getdate, nowdate, today
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
||||
@@ -4575,6 +4575,152 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
self.assertEqual(stock_ledger_entry.qty, 2.0)
|
||||
self.assertEqual(stock_ledger_entry.stock_value_difference, 0.0)
|
||||
|
||||
def test_non_batchwise_valuation_for_moving_average(self):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
item_code = "_Test Item for Non Batchwise Valuation"
|
||||
make_item_for_si(
|
||||
item_code,
|
||||
{
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TBATCH-TCNV.####",
|
||||
"valuation_method": "Moving Average",
|
||||
},
|
||||
)
|
||||
|
||||
doc = frappe.get_doc("Stock Settings")
|
||||
original_value = cint(doc.do_not_use_batchwise_valuation)
|
||||
|
||||
doc.db_set("do_not_use_batchwise_valuation", 1)
|
||||
|
||||
se = make_stock_entry(
|
||||
item_code=item_code,
|
||||
qty=10,
|
||||
target="_Test Warehouse - _TC",
|
||||
rate=13.02,
|
||||
valuation_method="Moving Average",
|
||||
use_serial_batch_fields=True,
|
||||
)
|
||||
|
||||
se_batch = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
# without use serial and batch fields
|
||||
si = create_sales_invoice(
|
||||
item=item_code,
|
||||
qty=1,
|
||||
rate=120,
|
||||
update_stock=1,
|
||||
use_serial_batch_fields=False,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
)
|
||||
|
||||
si.reload()
|
||||
si_batch = get_batch_from_bundle(si.items[0].serial_and_batch_bundle)
|
||||
|
||||
self.assertEqual(se_batch, si_batch)
|
||||
self.assertEqual(si.items[0].use_serial_batch_fields, 0)
|
||||
|
||||
serial_and_batch_bundle = si.items[0].serial_and_batch_bundle
|
||||
change_in_value = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{
|
||||
"voucher_type": "Sales Invoice",
|
||||
"voucher_no": si.name,
|
||||
"item_code": item_code,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"serial_and_batch_bundle": serial_and_batch_bundle,
|
||||
},
|
||||
"stock_value_difference",
|
||||
)
|
||||
|
||||
self.assertEqual(change_in_value, 13.02 * -1)
|
||||
|
||||
# with use serial and batch fields
|
||||
si = create_sales_invoice(
|
||||
item=item_code,
|
||||
qty=1,
|
||||
rate=120,
|
||||
update_stock=1,
|
||||
use_serial_batch_fields=True,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
)
|
||||
|
||||
si.reload()
|
||||
|
||||
self.assertEqual(si.items[0].use_serial_batch_fields, 1)
|
||||
|
||||
serial_and_batch_bundle = si.items[0].serial_and_batch_bundle
|
||||
change_in_value = frappe.db.get_value(
|
||||
"Stock Ledger Entry",
|
||||
{
|
||||
"voucher_type": "Sales Invoice",
|
||||
"voucher_no": si.name,
|
||||
"item_code": item_code,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"serial_and_batch_bundle": serial_and_batch_bundle,
|
||||
},
|
||||
"stock_value_difference",
|
||||
)
|
||||
|
||||
self.assertEqual(change_in_value, 13.02 * -1)
|
||||
|
||||
doc.db_set("do_not_use_batchwise_valuation", original_value)
|
||||
|
||||
def test_system_generated_exchange_gain_or_loss_je_after_repost(self):
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
|
||||
update_repost_settings,
|
||||
)
|
||||
|
||||
update_repost_settings()
|
||||
|
||||
si = create_sales_invoice(
|
||||
customer="_Test Customer USD",
|
||||
debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD",
|
||||
conversion_rate=80,
|
||||
)
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name)
|
||||
pe.reference_no = "10"
|
||||
pe.reference_date = nowdate()
|
||||
pe.paid_from_account_currency = si.currency
|
||||
pe.paid_to_account_currency = "INR"
|
||||
pe.source_exchange_rate = 85
|
||||
pe.target_exchange_rate = 1
|
||||
pe.paid_amount = si.outstanding_amount
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
ral = frappe.new_doc("Repost Accounting Ledger")
|
||||
ral.company = si.company
|
||||
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
|
||||
ral.save()
|
||||
ral.submit()
|
||||
|
||||
je = frappe.qb.DocType("Journal Entry")
|
||||
jea = frappe.qb.DocType("Journal Entry Account")
|
||||
q = (
|
||||
(
|
||||
frappe.qb.from_(je)
|
||||
.join(jea)
|
||||
.on(je.name == jea.parent)
|
||||
.select(je.docstatus)
|
||||
.where(
|
||||
(je.voucher_type == "Exchange Gain Or Loss")
|
||||
& (jea.reference_name == si.name)
|
||||
& (jea.reference_type == "Sales Invoice")
|
||||
& (je.is_system_generated == 1)
|
||||
)
|
||||
)
|
||||
.limit(1)
|
||||
.run()
|
||||
)
|
||||
|
||||
self.assertEqual(q[0][0], 1)
|
||||
|
||||
|
||||
def make_item_for_si(item_code, properties=None):
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
@@ -4690,6 +4836,7 @@ def create_sales_invoice(**args):
|
||||
"incoming_rate": args.incoming_rate or 0,
|
||||
"serial_and_batch_bundle": bundle_id,
|
||||
"allow_zero_valuation_rate": args.allow_zero_valuation_rate or 0,
|
||||
"use_serial_batch_fields": args.use_serial_batch_fields or 0,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -756,18 +756,14 @@ def get_prorata_factor(
|
||||
return diff / plan_days
|
||||
|
||||
|
||||
def process_all(subscription: str | None = None, posting_date: DateTimeLikeObject | None = None) -> None:
|
||||
def process_all(subscription: list, posting_date: DateTimeLikeObject | None = None) -> None:
|
||||
"""
|
||||
Task to updates the status of all `Subscription` apart from those that are cancelled
|
||||
"""
|
||||
filters = {"status": ("!=", "Cancelled")}
|
||||
|
||||
if subscription:
|
||||
filters["name"] = subscription
|
||||
|
||||
for subscription in frappe.get_all("Subscription", filters, pluck="name"):
|
||||
for subscription_name in subscription:
|
||||
try:
|
||||
subscription = frappe.get_doc("Subscription", subscription)
|
||||
subscription = frappe.get_doc("Subscription", subscription_name)
|
||||
subscription.process(posting_date)
|
||||
frappe.db.commit()
|
||||
except frappe.ValidationError:
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
|
||||
"description": "Only payment entries with apply tax withholding unchecked will be considered for checking cumulative threshold breach",
|
||||
"fieldname": "consider_party_ledger_amount",
|
||||
"fieldtype": "Check",
|
||||
"label": "Consider Entire Party Ledger Amount",
|
||||
@@ -102,10 +102,11 @@
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-27 21:47:34.396071",
|
||||
"modified": "2025-07-30 07:13:51.785735",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Withholding Category",
|
||||
"naming_rule": "Set by user",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -148,4 +149,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,15 @@ def process_gl_map(gl_map, merge_entries=True, precision=None, from_repost=False
|
||||
|
||||
|
||||
def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None, from_repost=False):
|
||||
round_off_account, default_currency = frappe.get_cached_value(
|
||||
"Company", gl_map[0].company, ["round_off_account", "default_currency"]
|
||||
)
|
||||
if not precision:
|
||||
precision = get_field_precision(
|
||||
frappe.get_meta("GL Entry").get_field("debit"),
|
||||
currency=default_currency,
|
||||
)
|
||||
|
||||
new_gl_map = []
|
||||
for d in gl_map:
|
||||
cost_center = d.get("cost_center")
|
||||
@@ -203,6 +212,11 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None, from_r
|
||||
new_gl_map.append(d)
|
||||
continue
|
||||
|
||||
if d.account == round_off_account:
|
||||
d.cost_center = cost_center_allocation[0][0]
|
||||
new_gl_map.append(d)
|
||||
continue
|
||||
|
||||
for sub_cost_center, percentage in cost_center_allocation:
|
||||
gle = copy.deepcopy(d)
|
||||
gle.cost_center = sub_cost_center
|
||||
|
||||
@@ -197,6 +197,11 @@ frappe.query_reports["General Ledger"] = {
|
||||
label: __("Show Net Values in Party Account"),
|
||||
fieldtype: "Check",
|
||||
},
|
||||
{
|
||||
fieldname: "show_amount_in_company_currency",
|
||||
label: __("Show Credit / Debit in Company Currency"),
|
||||
fieldtype: "Check",
|
||||
},
|
||||
{
|
||||
fieldname: "add_values_in_transaction_currency",
|
||||
label: __("Add Columns in Transaction Currency"),
|
||||
|
||||
@@ -203,6 +203,12 @@ def get_gl_entries(filters, accounting_dimensions):
|
||||
as_dict=1,
|
||||
)
|
||||
|
||||
party_name_map = get_party_name_map()
|
||||
|
||||
for gl_entry in gl_entries:
|
||||
if gl_entry.party_type and gl_entry.party:
|
||||
gl_entry.party_name = party_name_map.get(gl_entry.party_type, {}).get(gl_entry.party)
|
||||
|
||||
if filters.get("presentation_currency"):
|
||||
return convert_to_presentation_currency(gl_entries, currency_map, filters)
|
||||
else:
|
||||
@@ -337,6 +343,20 @@ def get_conditions(filters):
|
||||
return "and {}".format(" and ".join(conditions)) if conditions else ""
|
||||
|
||||
|
||||
def get_party_name_map():
|
||||
party_map = {}
|
||||
|
||||
customers = frappe.get_all("Customer", fields=["name", "customer_name"])
|
||||
party_map["Customer"] = {c.name: c.customer_name for c in customers}
|
||||
|
||||
suppliers = frappe.get_all("Supplier", fields=["name", "supplier_name"])
|
||||
party_map["Supplier"] = {s.name: s.supplier_name for s in suppliers}
|
||||
|
||||
employees = frappe.get_all("Employee", fields=["name", "employee_name"])
|
||||
party_map["Employee"] = {e.name: e.employee_name for e in employees}
|
||||
return party_map
|
||||
|
||||
|
||||
def get_accounts_with_children(accounts):
|
||||
if not isinstance(accounts, list):
|
||||
accounts = [d.strip() for d in accounts.strip().split(",") if d]
|
||||
@@ -605,6 +625,18 @@ def get_columns(filters):
|
||||
company = filters.get("company") or get_default_company()
|
||||
filters["presentation_currency"] = currency = get_company_currency(company)
|
||||
|
||||
company_currency = get_company_currency(filters.get("company") or get_default_company())
|
||||
|
||||
if (
|
||||
filters.get("show_amount_in_company_currency")
|
||||
and filters["presentation_currency"] != company_currency
|
||||
):
|
||||
frappe.throw(
|
||||
_(
|
||||
f'Presentation Currency cannot be {frappe.bold(filters["presentation_currency"])} , When {frappe.bold("Show Credit / Debit in Company Currency")} is enabled.'
|
||||
)
|
||||
)
|
||||
|
||||
columns = [
|
||||
{
|
||||
"label": _("GL Entry"),
|
||||
@@ -689,6 +721,19 @@ def get_columns(filters):
|
||||
{"label": _("Party"), "fieldname": "party", "width": 100},
|
||||
]
|
||||
|
||||
supplier_master_name = frappe.db.get_single_value("Buying Settings", "supp_master_name")
|
||||
customer_master_name = frappe.db.get_single_value("Selling Settings", "cust_master_name")
|
||||
|
||||
if supplier_master_name != "Supplier Name" or customer_master_name != "Customer Name":
|
||||
columns.append(
|
||||
{
|
||||
"label": _("Party Name"),
|
||||
"fieldname": "party_name",
|
||||
"fieldtype": "Data",
|
||||
"width": 150,
|
||||
}
|
||||
)
|
||||
|
||||
if filters.get("include_dimensions"):
|
||||
columns.append({"label": _("Project"), "options": "Project", "fieldname": "project", "width": 100})
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ def execute(filters=None):
|
||||
"invoice_or_item",
|
||||
"customer",
|
||||
"customer_group",
|
||||
"customer_name",
|
||||
"posting_date",
|
||||
"item_code",
|
||||
"item_name",
|
||||
@@ -95,6 +96,7 @@ def execute(filters=None):
|
||||
"customer": [
|
||||
"customer",
|
||||
"customer_group",
|
||||
"customer_name",
|
||||
"qty",
|
||||
"base_rate",
|
||||
"buying_rate",
|
||||
@@ -250,6 +252,10 @@ def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_
|
||||
|
||||
def get_columns(group_wise_columns, filters):
|
||||
columns = []
|
||||
|
||||
supplier_master_name = frappe.db.get_single_value("Buying Settings", "supp_master_name")
|
||||
customer_master_name = frappe.db.get_single_value("Selling Settings", "cust_master_name")
|
||||
|
||||
column_map = frappe._dict(
|
||||
{
|
||||
"parent": {
|
||||
@@ -395,6 +401,12 @@ def get_columns(group_wise_columns, filters):
|
||||
"options": "Customer Group",
|
||||
"width": 100,
|
||||
},
|
||||
"customer_name": {
|
||||
"label": _("Customer Name"),
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"width": 150,
|
||||
},
|
||||
"territory": {
|
||||
"label": _("Territory"),
|
||||
"fieldname": "territory",
|
||||
@@ -419,6 +431,10 @@ def get_columns(group_wise_columns, filters):
|
||||
)
|
||||
|
||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||
if col == "customer_name" and (
|
||||
supplier_master_name == "Supplier Name" and customer_master_name == "Customer Name"
|
||||
):
|
||||
continue
|
||||
columns.append(column_map.get(col))
|
||||
|
||||
columns.append(
|
||||
@@ -440,6 +456,7 @@ def get_column_names():
|
||||
"invoice_or_item": "sales_invoice",
|
||||
"customer": "customer",
|
||||
"customer_group": "customer_group",
|
||||
"customer_name": "customer_name",
|
||||
"posting_date": "posting_date",
|
||||
"item_code": "item_code",
|
||||
"item_name": "item_name",
|
||||
@@ -905,7 +922,7 @@ class GrossProfitGenerator:
|
||||
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.posting_time,
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.update_stock,
|
||||
`tabSales Invoice`.customer, `tabSales Invoice`.customer_group,
|
||||
`tabSales Invoice`.customer, `tabSales Invoice`.customer_group, `tabSales Invoice`.customer_name,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice Item`.item_code,
|
||||
`tabSales Invoice`.base_net_total as "invoice_base_net_total",
|
||||
`tabSales Invoice Item`.item_name, `tabSales Invoice Item`.description,
|
||||
@@ -1003,6 +1020,7 @@ class GrossProfitGenerator:
|
||||
"update_stock": row.update_stock,
|
||||
"customer": row.customer,
|
||||
"customer_group": row.customer_group,
|
||||
"customer_name": row.customer_name,
|
||||
"item_code": None,
|
||||
"item_name": None,
|
||||
"description": None,
|
||||
@@ -1032,6 +1050,7 @@ class GrossProfitGenerator:
|
||||
"project": row.project,
|
||||
"customer": row.customer,
|
||||
"customer_group": row.customer_group,
|
||||
"customer_name": row.customer_name,
|
||||
"item_code": item.item_code,
|
||||
"item_name": item.item_name,
|
||||
"description": item.description,
|
||||
|
||||
@@ -46,6 +46,7 @@ def get_ordered_to_be_billed_data(args, filters=None):
|
||||
child_doctype.item_name,
|
||||
child_doctype.description,
|
||||
project_field,
|
||||
doctype.company,
|
||||
)
|
||||
.where(
|
||||
(doctype.docstatus == 1)
|
||||
|
||||
@@ -46,6 +46,7 @@ class PaymentLedger:
|
||||
against_voucher_no=ple.against_voucher_no,
|
||||
amount=ple.amount,
|
||||
currency=ple.account_currency,
|
||||
company=ple.company,
|
||||
)
|
||||
|
||||
if self.filters.include_account_currency:
|
||||
@@ -77,6 +78,7 @@ class PaymentLedger:
|
||||
against_voucher_no="Outstanding:",
|
||||
amount=total,
|
||||
currency=voucher_data[0].currency,
|
||||
company=voucher_data[0].company,
|
||||
)
|
||||
|
||||
if self.filters.include_account_currency:
|
||||
@@ -85,7 +87,12 @@ class PaymentLedger:
|
||||
voucher_data.append(entry)
|
||||
|
||||
# empty row
|
||||
voucher_data.append(frappe._dict())
|
||||
voucher_data.append(
|
||||
frappe._dict(
|
||||
currency=voucher_data[0].currency,
|
||||
company=voucher_data[0].company,
|
||||
)
|
||||
)
|
||||
self.data.extend(voucher_data)
|
||||
|
||||
def build_conditions(self):
|
||||
@@ -130,7 +137,6 @@ class PaymentLedger:
|
||||
)
|
||||
|
||||
def get_columns(self):
|
||||
company_currency = frappe.get_cached_value("Company", self.filters.get("company"), "default_currency")
|
||||
options = None
|
||||
self.columns.append(
|
||||
dict(
|
||||
@@ -195,7 +201,7 @@ class PaymentLedger:
|
||||
label=_("Amount"),
|
||||
fieldname="amount",
|
||||
fieldtype="Currency",
|
||||
options=company_currency,
|
||||
options="Company:company:default_currency",
|
||||
width="100",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -45,6 +45,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
|
||||
gle_map = get_gle_map(tds_docs)
|
||||
|
||||
out = []
|
||||
entries = {}
|
||||
for name, details in gle_map.items():
|
||||
for entry in details:
|
||||
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
|
||||
@@ -119,8 +120,13 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
|
||||
"supplier_invoice_date": bill_date,
|
||||
}
|
||||
)
|
||||
out.append(row)
|
||||
|
||||
key = entry.voucher_no
|
||||
if key in entries:
|
||||
entries[key]["tax_amount"] += tax_amount
|
||||
else:
|
||||
entries[key] = row
|
||||
out = list(entries.values())
|
||||
out.sort(key=lambda x: (x["section_code"], x["transaction_date"]))
|
||||
|
||||
return out
|
||||
|
||||
@@ -118,7 +118,7 @@ def convert_to_presentation_currency(gl_entries, currency_info, filters=None):
|
||||
len(account_currencies) == 1
|
||||
and account_currency == presentation_currency
|
||||
and not exchange_gain_or_loss
|
||||
):
|
||||
) and not (filters and filters.get("show_amount_in_company_currency")):
|
||||
entry["debit"] = debit_in_account_currency
|
||||
entry["credit"] = credit_in_account_currency
|
||||
else:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
from collections import defaultdict
|
||||
from json import loads
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
@@ -469,7 +470,6 @@ def reconcile_against_document(
|
||||
reconciled_entries[(row.voucher_type, row.voucher_no)] = []
|
||||
|
||||
reconciled_entries[(row.voucher_type, row.voucher_no)].append(row)
|
||||
|
||||
for key, entries in reconciled_entries.items():
|
||||
voucher_type, voucher_no = key
|
||||
|
||||
@@ -1339,6 +1339,7 @@ def create_payment_gateway_account(gateway, payment_channel="Email"):
|
||||
"payment_account": bank_account.name,
|
||||
"currency": bank_account.account_currency,
|
||||
"payment_channel": payment_channel,
|
||||
"company": company,
|
||||
}
|
||||
).insert(ignore_permissions=True, ignore_if_duplicate=True)
|
||||
|
||||
@@ -1803,6 +1804,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
|
||||
|
||||
dr_or_cr = 0
|
||||
account_type = None
|
||||
|
||||
for gle in gl_entries:
|
||||
if gle.account in receivable_or_payable_accounts:
|
||||
account_type = get_account_type(gle.account)
|
||||
@@ -1907,7 +1909,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
|
||||
if not voucher_type or not voucher_no:
|
||||
return
|
||||
|
||||
if voucher_type in ["Purchase Order", "Sales Order"]:
|
||||
if voucher_type in get_advance_payment_doctypes():
|
||||
ref_doc = frappe.get_lazy_doc(voucher_type, voucher_no)
|
||||
ref_doc.set_total_advance_paid()
|
||||
return
|
||||
@@ -2419,25 +2421,37 @@ def sync_auto_reconcile_config(auto_reconciliation_job_trigger: int = 15):
|
||||
).save()
|
||||
|
||||
|
||||
def get_link_fields_grouped_by_option(doctype):
|
||||
meta = frappe.get_meta(doctype)
|
||||
link_fields_map = defaultdict(list)
|
||||
|
||||
for df in meta.fields:
|
||||
if df.fieldtype == "Link" and df.options and not df.ignore_user_permissions:
|
||||
link_fields_map[df.options].append(df.fieldname)
|
||||
|
||||
return link_fields_map
|
||||
|
||||
|
||||
def build_qb_match_conditions(doctype, user=None) -> list:
|
||||
match_filters = build_match_conditions(doctype, user, False)
|
||||
link_fields_map = get_link_fields_grouped_by_option(doctype)
|
||||
criterion = []
|
||||
apply_strict_user_permissions = frappe.get_system_settings("apply_strict_user_permissions")
|
||||
|
||||
if match_filters:
|
||||
from frappe import qb
|
||||
|
||||
_dt = qb.DocType(doctype)
|
||||
|
||||
for filter in match_filters:
|
||||
for d, names in filter.items():
|
||||
fieldname = d.lower().replace(" ", "_")
|
||||
field = _dt[fieldname]
|
||||
for link_option, allowed_values in filter.items():
|
||||
fieldnames = link_fields_map.get(link_option, [])
|
||||
|
||||
cond = field.isin(names)
|
||||
if not apply_strict_user_permissions:
|
||||
cond = (Coalesce(field, "") == "") | field.isin(names)
|
||||
for fieldname in fieldnames:
|
||||
field = _dt[fieldname]
|
||||
cond = field.isin(allowed_values)
|
||||
|
||||
criterion.append(cond)
|
||||
if not apply_strict_user_permissions:
|
||||
cond = (Coalesce(field, "") == "") | cond
|
||||
|
||||
criterion.append(cond)
|
||||
|
||||
return criterion
|
||||
|
||||
@@ -1097,7 +1097,7 @@ def make_journal_entry(asset_name):
|
||||
je.voucher_type = "Depreciation Entry"
|
||||
je.naming_series = depreciation_series
|
||||
je.company = asset.company
|
||||
je.remark = f"Depreciation Entry against asset {asset_name}"
|
||||
je.remark = _("Depreciation Entry against asset {0}").format(asset_name)
|
||||
|
||||
je.append(
|
||||
"accounts",
|
||||
|
||||
@@ -277,7 +277,9 @@ def _make_journal_entry_for_depreciation(
|
||||
je.posting_date = depr_schedule.schedule_date
|
||||
je.company = asset.company
|
||||
je.finance_book = asset_depr_schedule_doc.finance_book
|
||||
je.remark = f"Depreciation Entry against {asset.name} worth {depr_schedule.depreciation_amount}"
|
||||
je.remark = _("Depreciation Entry against {0} worth {1}").format(
|
||||
asset.name, depr_schedule.depreciation_amount
|
||||
)
|
||||
|
||||
credit_entry = {
|
||||
"account": credit_account,
|
||||
|
||||
@@ -346,6 +346,33 @@ class TestAsset(AssetSetup):
|
||||
si.cancel()
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
|
||||
|
||||
def test_asset_status_after_sales_invoice_cancel(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
asset = create_asset(
|
||||
calculate_depreciation=1,
|
||||
available_for_use_date="2020-04-01",
|
||||
purchase_date="2020-04-01",
|
||||
expected_value_after_useful_life=0,
|
||||
total_number_of_depreciations=5,
|
||||
opening_number_of_booked_depreciations=2,
|
||||
frequency_of_depreciation=12,
|
||||
depreciation_start_date="2023-03-31",
|
||||
opening_accumulated_depreciation=24000,
|
||||
gross_purchase_amount=60000,
|
||||
submit=1,
|
||||
)
|
||||
|
||||
si = create_sales_invoice(
|
||||
item_code="Macbook Pro", asset=asset.name, qty=1, rate=40000, posting_date=getdate("2023-05-23")
|
||||
)
|
||||
asset.load_from_db()
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
|
||||
|
||||
si.cancel()
|
||||
asset.load_from_db()
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
|
||||
|
||||
def test_gle_made_by_asset_sale_for_existing_asset(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
|
||||
@@ -98,20 +98,41 @@ class AssetDepreciationSchedule(Document):
|
||||
)
|
||||
|
||||
def on_submit(self):
|
||||
self.validate_asset()
|
||||
self.db_set("status", "Active")
|
||||
|
||||
def before_cancel(self):
|
||||
def validate_asset(self):
|
||||
asset = frappe.get_doc("Asset", self.asset)
|
||||
if not asset.calculate_depreciation:
|
||||
frappe.throw(
|
||||
_("Asset {0} is not set to calculate depreciation.").format(
|
||||
get_link_to_form("Asset", self.asset)
|
||||
)
|
||||
)
|
||||
if asset.docstatus != 1:
|
||||
frappe.throw(
|
||||
_("Asset {0} is not submitted. Please submit the asset before proceeding.").format(
|
||||
get_link_to_form("Asset", self.asset)
|
||||
)
|
||||
)
|
||||
|
||||
def on_cancel(self):
|
||||
self.db_set("status", "Cancelled")
|
||||
if not self.flags.should_not_cancel_depreciation_entries:
|
||||
self.cancel_depreciation_entries()
|
||||
|
||||
def cancel_depreciation_entries(self):
|
||||
for d in self.get("depreciation_schedule"):
|
||||
if d.journal_entry:
|
||||
je_status = frappe.db.get_value("Journal Entry", d.journal_entry, "docstatus")
|
||||
if je_status == 0:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Cannot cancel Asset Depreciation Schedule {0} as it has a draft journal entry {1}."
|
||||
).format(self.name, d.journal_entry)
|
||||
)
|
||||
frappe.get_doc("Journal Entry", d.journal_entry).cancel()
|
||||
|
||||
def on_cancel(self):
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
def update_shift_depr_schedule(self):
|
||||
if not self.shift_based or self.docstatus != 0:
|
||||
return
|
||||
|
||||
@@ -3,13 +3,13 @@ frappe.listview_settings["Asset Maintenance Log"] = {
|
||||
has_indicator_for_draft: 1,
|
||||
get_indicator: function (doc) {
|
||||
if (doc.maintenance_status == "Planned") {
|
||||
return [__(doc.maintenance_status), "orange", "status,=," + doc.maintenance_status];
|
||||
return [__(doc.maintenance_status), "orange", "maintenance_status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status == "Completed") {
|
||||
return [__(doc.maintenance_status), "green", "status,=," + doc.maintenance_status];
|
||||
return [__(doc.maintenance_status), "green", "maintenance_status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status == "Cancelled") {
|
||||
return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
|
||||
return [__(doc.maintenance_status), "red", "maintenance_status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status == "Overdue") {
|
||||
return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
|
||||
return [__(doc.maintenance_status), "red", "maintenance_status,=," + doc.maintenance_status];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -31,6 +31,91 @@ def execute(filters=None):
|
||||
return columns, data, None, chart
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
pr_supplier_map = get_purchase_receipt_supplier_map()
|
||||
pi_supplier_map = get_purchase_invoice_supplier_map()
|
||||
|
||||
assets_linked_to_fb = get_assets_linked_to_fb(filters)
|
||||
|
||||
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
|
||||
|
||||
if filters.include_default_book_assets and company_fb:
|
||||
finance_book = company_fb
|
||||
elif filters.finance_book:
|
||||
finance_book = filters.finance_book
|
||||
else:
|
||||
finance_book = None
|
||||
|
||||
depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
|
||||
|
||||
revaluation_amount_map = get_asset_value_adjustment_map(filters, finance_book)
|
||||
|
||||
group_by = frappe.scrub(filters.get("group_by"))
|
||||
|
||||
if group_by in ("asset_category", "location"):
|
||||
data = get_group_by_data(
|
||||
group_by, conditions, assets_linked_to_fb, depreciation_amount_map, revaluation_amount_map
|
||||
)
|
||||
return data
|
||||
|
||||
fields = [
|
||||
"name as asset_id",
|
||||
"asset_name",
|
||||
"status",
|
||||
"department",
|
||||
"company",
|
||||
"cost_center",
|
||||
"calculate_depreciation",
|
||||
"purchase_receipt",
|
||||
"asset_category",
|
||||
"purchase_date",
|
||||
"gross_purchase_amount",
|
||||
"location",
|
||||
"available_for_use_date",
|
||||
"purchase_invoice",
|
||||
"opening_accumulated_depreciation",
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
for asset in assets_record:
|
||||
if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
|
||||
continue
|
||||
|
||||
depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
|
||||
revaluation_amount = revaluation_amount_map.get(asset.asset_id, 0.0)
|
||||
asset_value = (
|
||||
asset.gross_purchase_amount
|
||||
- asset.opening_accumulated_depreciation
|
||||
- depreciation_amount
|
||||
+ revaluation_amount
|
||||
)
|
||||
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt)
|
||||
or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value,
|
||||
"company": asset.company,
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = {"docstatus": 1}
|
||||
status = filters.status
|
||||
@@ -76,83 +161,6 @@ def get_conditions(filters):
|
||||
return conditions
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
|
||||
conditions = get_conditions(filters)
|
||||
pr_supplier_map = get_purchase_receipt_supplier_map()
|
||||
pi_supplier_map = get_purchase_invoice_supplier_map()
|
||||
|
||||
assets_linked_to_fb = get_assets_linked_to_fb(filters)
|
||||
|
||||
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
|
||||
|
||||
if filters.include_default_book_assets and company_fb:
|
||||
finance_book = company_fb
|
||||
elif filters.finance_book:
|
||||
finance_book = filters.finance_book
|
||||
else:
|
||||
finance_book = None
|
||||
|
||||
depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book)
|
||||
|
||||
group_by = frappe.scrub(filters.get("group_by"))
|
||||
|
||||
if group_by in ("asset_category", "location"):
|
||||
data = get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map)
|
||||
return data
|
||||
|
||||
fields = [
|
||||
"name as asset_id",
|
||||
"asset_name",
|
||||
"status",
|
||||
"department",
|
||||
"company",
|
||||
"cost_center",
|
||||
"calculate_depreciation",
|
||||
"purchase_receipt",
|
||||
"asset_category",
|
||||
"purchase_date",
|
||||
"gross_purchase_amount",
|
||||
"location",
|
||||
"available_for_use_date",
|
||||
"purchase_invoice",
|
||||
"opening_accumulated_depreciation",
|
||||
]
|
||||
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
|
||||
|
||||
for asset in assets_record:
|
||||
if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
|
||||
continue
|
||||
|
||||
depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0
|
||||
asset_value = (
|
||||
asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount
|
||||
)
|
||||
|
||||
row = {
|
||||
"asset_id": asset.asset_id,
|
||||
"asset_name": asset.asset_name,
|
||||
"status": asset.status,
|
||||
"department": asset.department,
|
||||
"cost_center": asset.cost_center,
|
||||
"vendor_name": pr_supplier_map.get(asset.purchase_receipt)
|
||||
or pi_supplier_map.get(asset.purchase_invoice),
|
||||
"gross_purchase_amount": asset.gross_purchase_amount,
|
||||
"opening_accumulated_depreciation": asset.opening_accumulated_depreciation,
|
||||
"depreciated_amount": depreciation_amount,
|
||||
"available_for_use_date": asset.available_for_use_date,
|
||||
"location": asset.location,
|
||||
"asset_category": asset.asset_category,
|
||||
"purchase_date": asset.purchase_date,
|
||||
"asset_value": asset_value,
|
||||
"company": asset.company,
|
||||
}
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def prepare_chart_data(data, filters):
|
||||
if not data:
|
||||
return
|
||||
@@ -290,7 +298,59 @@ def get_asset_depreciation_amount_map(filters, finance_book):
|
||||
return dict(asset_depr_amount_map)
|
||||
|
||||
|
||||
def get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map):
|
||||
def get_asset_value_adjustment_map(filters, finance_book):
|
||||
start_date = filters.from_date if filters.filter_based_on == "Date Range" else filters.year_start_date
|
||||
end_date = filters.to_date if filters.filter_based_on == "Date Range" else filters.year_end_date
|
||||
|
||||
asset = frappe.qb.DocType("Asset")
|
||||
gle = frappe.qb.DocType("GL Entry")
|
||||
aca = frappe.qb.DocType("Asset Category Account")
|
||||
company = frappe.qb.DocType("Company")
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(gle)
|
||||
.join(asset)
|
||||
.on(gle.against_voucher == asset.name)
|
||||
.join(aca)
|
||||
.on((aca.parent == asset.asset_category) & (aca.company_name == asset.company))
|
||||
.join(company)
|
||||
.on(company.name == asset.company)
|
||||
.select(asset.name.as_("asset"), Sum(gle.debit - gle.credit).as_("adjustment_amount"))
|
||||
.where(gle.account == aca.fixed_asset_account)
|
||||
.where(gle.is_cancelled == 0)
|
||||
.where(company.name == filters.company)
|
||||
.where(asset.docstatus == 1)
|
||||
)
|
||||
|
||||
if filters.only_existing_assets:
|
||||
query = query.where(asset.is_existing_asset == 1)
|
||||
if filters.asset_category:
|
||||
query = query.where(asset.asset_category == filters.asset_category)
|
||||
if filters.cost_center:
|
||||
query = query.where(asset.cost_center == filters.cost_center)
|
||||
if filters.status:
|
||||
if filters.status == "In Location":
|
||||
query = query.where(asset.status.notin(["Sold", "Scrapped", "Capitalized"]))
|
||||
else:
|
||||
query = query.where(asset.status.isin(["Sold", "Scrapped", "Capitalized"]))
|
||||
if finance_book:
|
||||
query = query.where((gle.finance_book.isin([cstr(finance_book), ""])) | (gle.finance_book.isnull()))
|
||||
else:
|
||||
query = query.where((gle.finance_book.isin([""])) | (gle.finance_book.isnull()))
|
||||
if filters.filter_based_on in ("Date Range", "Fiscal Year"):
|
||||
query = query.where(gle.posting_date >= start_date)
|
||||
query = query.where(gle.posting_date <= end_date)
|
||||
|
||||
query = query.groupby(asset.name)
|
||||
|
||||
asset_adjustment_map = query.run()
|
||||
|
||||
return dict(asset_adjustment_map)
|
||||
|
||||
|
||||
def get_group_by_data(
|
||||
group_by, conditions, assets_linked_to_fb, depreciation_amount_map, revaluation_amount_map
|
||||
):
|
||||
fields = [
|
||||
group_by,
|
||||
"name",
|
||||
@@ -307,8 +367,12 @@ def get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_am
|
||||
continue
|
||||
|
||||
a["depreciated_amount"] = depreciation_amount_map.get(a["name"], 0.0)
|
||||
a["revaluation_amount"] = revaluation_amount_map.get(a["name"], 0.0)
|
||||
a["asset_value"] = (
|
||||
a["gross_purchase_amount"] - a["opening_accumulated_depreciation"] - a["depreciated_amount"]
|
||||
a["gross_purchase_amount"]
|
||||
- a["opening_accumulated_depreciation"]
|
||||
- a["depreciated_amount"]
|
||||
+ a["revaluation_amount"]
|
||||
)
|
||||
|
||||
del a["name"]
|
||||
|
||||
@@ -578,7 +578,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
||||
},
|
||||
allow_child_item_selection: true,
|
||||
child_fieldname: "items",
|
||||
child_columns: ["item_code", "qty", "ordered_qty"],
|
||||
child_columns: ["item_code", "item_name", "qty", "ordered_qty"],
|
||||
});
|
||||
},
|
||||
__("Get Items From")
|
||||
|
||||
@@ -720,6 +720,7 @@ def close_or_unclose_purchase_orders(names, status):
|
||||
def set_missing_values(source, target):
|
||||
target.run_method("set_missing_values")
|
||||
target.run_method("calculate_taxes_and_totals")
|
||||
target.run_method("set_use_serial_batch_fields")
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -51,7 +51,7 @@ def get_columns(filters):
|
||||
},
|
||||
{
|
||||
"label": _("Requestor"),
|
||||
"options": "Employee",
|
||||
"options": "User",
|
||||
"fieldname": "requestor",
|
||||
"fieldtype": "Link",
|
||||
"width": 140,
|
||||
|
||||
@@ -701,7 +701,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
|
||||
where (tabAccount.report_type = "Profit and Loss"
|
||||
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
|
||||
and tabAccount.is_group=0
|
||||
and tabAccount.docstatus!=2
|
||||
and tabAccount.disabled = 0
|
||||
and tabAccount.{searchfield} LIKE %(txt)s
|
||||
{condition} {get_match_cond(doctype)}""",
|
||||
{"company": filters.get("company", ""), "txt": "%" + txt + "%"},
|
||||
|
||||
@@ -959,8 +959,9 @@ class StockController(AccountsController):
|
||||
make_sl_entries(sl_entries, allow_negative_stock, via_landed_cost_voucher)
|
||||
update_batch_qty(self.doctype, self.name, via_landed_cost_voucher=via_landed_cost_voucher)
|
||||
|
||||
def make_gl_entries_on_cancel(self):
|
||||
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
||||
def make_gl_entries_on_cancel(self, from_repost=False):
|
||||
if not from_repost:
|
||||
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
||||
if frappe.db.sql(
|
||||
"""select name from `tabGL Entry` where voucher_type=%s
|
||||
and voucher_no=%s""",
|
||||
|
||||
@@ -284,6 +284,9 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
|
||||
this.frm.set_value("currency", frappe.defaults.get_user_default("Currency"));
|
||||
}
|
||||
|
||||
if (this.frm.is_new() && this.frm.doc.opportunity_type === undefined) {
|
||||
this.frm.doc.opportunity_type = __("Sales");
|
||||
}
|
||||
this.setup_queries();
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"default": "Sales",
|
||||
"fieldname": "opportunity_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
@@ -670,4 +669,4 @@
|
||||
"title_field": "title",
|
||||
"track_seen": 1,
|
||||
"track_views": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ class Opportunity(TransactionBase, CRMNote):
|
||||
link_communications(self.opportunity_from, self.party_name, self)
|
||||
|
||||
def validate(self):
|
||||
self.set_opportunity_type()
|
||||
self.make_new_lead_if_required()
|
||||
self.validate_item_details()
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
@@ -150,6 +151,10 @@ class Opportunity(TransactionBase, CRMNote):
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
def set_opportunity_type(self):
|
||||
if self.is_new() and not self.opportunity_type:
|
||||
self.opportunity_type = _("Sales")
|
||||
|
||||
def set_exchange_rate(self):
|
||||
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
if self.currency == company_currency:
|
||||
|
||||
@@ -153,7 +153,12 @@ class OpportunitySummaryBySalesStage:
|
||||
}[self.filters.get("based_on")]
|
||||
|
||||
if self.filters.get("based_on") == "Opportunity Owner":
|
||||
if d.get(based_on) == "[]" or d.get(based_on) is None or d.get(based_on) == "Not Assigned":
|
||||
if (
|
||||
d.get(based_on) == "[]"
|
||||
or d.get(based_on) is None
|
||||
or d.get(based_on) == "Not Assigned"
|
||||
or d.get(based_on) == ""
|
||||
):
|
||||
assignments = ["Not Assigned"]
|
||||
else:
|
||||
assignments = json.loads(d.get(based_on))
|
||||
|
||||
@@ -60,7 +60,7 @@ def import_genericode():
|
||||
"doctype": "File",
|
||||
"attached_to_doctype": "Code List",
|
||||
"attached_to_name": code_list.name,
|
||||
"folder": "Home/Attachments",
|
||||
"folder": frappe.db.get_value("File", {"is_attachments_folder": 1}),
|
||||
"file_name": frappe.local.uploaded_filename,
|
||||
"file_url": frappe.local.uploaded_file_url,
|
||||
"is_private": 1,
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
"fieldname": "hour_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Hour Rate",
|
||||
"non_negative": 1,
|
||||
"oldfieldname": "hour_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency",
|
||||
@@ -78,6 +79,7 @@
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Operation Time",
|
||||
"non_negative": 1,
|
||||
"oldfieldname": "time_in_mins",
|
||||
"oldfieldtype": "Currency",
|
||||
"reqd": 1
|
||||
@@ -194,7 +196,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-11-04 17:17:16.986941",
|
||||
"modified": "2025-07-31 16:17:47.287117",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Operation",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Qty",
|
||||
"non_negative": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@@ -49,6 +50,7 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"non_negative": 1,
|
||||
"options": "currency"
|
||||
},
|
||||
{
|
||||
@@ -92,7 +94,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-03 14:19:28.460965",
|
||||
"modified": "2025-07-31 16:21:44.047007",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Scrap Item",
|
||||
@@ -103,4 +105,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ frappe.ui.form.on("Job Card", {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("item_code", "scrap_items", () => {
|
||||
return {
|
||||
filters: {
|
||||
disabled: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_indicator_formatter("sub_operation", function (doc) {
|
||||
if (doc.status == "Pending") {
|
||||
return "red";
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
"fieldname": "total_completed_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Total Completed Qty",
|
||||
"non_negative": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@@ -510,7 +511,7 @@
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-03-17 15:55:11.143456",
|
||||
"modified": "2025-08-04 15:47:54.514290",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card",
|
||||
@@ -568,4 +569,4 @@
|
||||
"states": [],
|
||||
"title_field": "operation",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-14 01:20:48.588052",
|
||||
"modified": "2025-07-29 13:09:57.323835",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card Scrap Item",
|
||||
@@ -80,4 +80,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
"fieldname": "completed_qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Completed Qty"
|
||||
"label": "Completed Qty",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "employee",
|
||||
@@ -63,7 +64,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-05-21 12:40:55.765860",
|
||||
"modified": "2025-08-04 15:47:11.748937",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Job Card Time Log",
|
||||
|
||||
@@ -70,7 +70,8 @@
|
||||
"fieldname": "batch_size",
|
||||
"fieldtype": "Int",
|
||||
"label": "Batch Size",
|
||||
"mandatory_depends_on": "create_job_card_based_on_batch_size"
|
||||
"mandatory_depends_on": "create_job_card_based_on_batch_size",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -104,7 +105,7 @@
|
||||
"icon": "fa fa-wrench",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-11-24 19:15:24.357187",
|
||||
"modified": "2025-08-04 16:14:57.659318",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Operation",
|
||||
@@ -137,4 +138,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"fieldname": "time_in_mins",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Operation Time"
|
||||
"label": "Operation Time",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
@@ -39,7 +40,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-15 16:39:41.635362",
|
||||
"modified": "2025-08-04 16:15:11.425349",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Sub Operation",
|
||||
@@ -49,4 +50,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,12 +356,12 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation
|
||||
erpnext.patches.v14_0.update_zero_asset_quantity_field
|
||||
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
|
||||
erpnext.patches.v14_0.update_total_asset_cost_field
|
||||
erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes #2025-06-19
|
||||
erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
|
||||
erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
|
||||
erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22
|
||||
erpnext.patches.v15_0.create_accounting_dimensions_in_payment_request
|
||||
erpnext.patches.v14_0.update_pos_return_ledger_entries #2024-08-16
|
||||
erpnext.patches.v15_0.create_advance_payment_ledger_records #2025-07-04
|
||||
erpnext.patches.v15_0.create_advance_payment_ledger_records #2025-07-08
|
||||
# below migration patch should always run last
|
||||
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
|
||||
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
|
||||
@@ -416,5 +416,7 @@ erpnext.patches.v15_0.update_payment_ledger_entries_against_advance_doctypes
|
||||
erpnext.patches.v15_0.rename_price_list_to_buying_price_list
|
||||
erpnext.patches.v15_0.patch_missing_buying_price_list_in_material_request
|
||||
erpnext.patches.v15_0.remove_sales_partner_from_consolidated_sales_invoice
|
||||
erpnext.patches.v15_0.repost_gl_entries_with_no_account_subcontracting #2025-07-31
|
||||
erpnext.patches.v15_0.repost_gl_entries_with_no_account_subcontracting #2025-08-04
|
||||
execute:frappe.db.set_single_value("Accounts Settings", "fetch_valuation_rate_for_internal_transaction", 1)
|
||||
erpnext.patches.v15_0.add_company_payment_gateway_account
|
||||
erpnext.patches.v15_0.update_uae_zero_rated_fetch
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
for gateway_account in frappe.get_list("Payment Gateway Account", fields=["name", "payment_account"]):
|
||||
company = frappe.db.get_value("Account", gateway_account.payment_account, "company")
|
||||
frappe.db.set_value("Payment Gateway Account", gateway_account.name, "company", company)
|
||||
@@ -9,6 +9,6 @@ from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger
|
||||
|
||||
|
||||
def execute():
|
||||
for dt in get_allowed_types_from_settings():
|
||||
for dt in get_allowed_types_from_settings(child_doc=True):
|
||||
for dimension in get_accounting_dimensions():
|
||||
frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1)
|
||||
|
||||
@@ -2,24 +2,31 @@ import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
def cancel_incorrect_gl_entries(gl_entries):
|
||||
table = frappe.qb.DocType("GL Entry")
|
||||
frappe.qb.update(table).set(table.is_cancelled, 1).where(table.name.isin(gl_entries)).run()
|
||||
|
||||
def recreate_gl_entries(voucher_nos):
|
||||
for doc in voucher_nos:
|
||||
doc = frappe.get_doc("Subcontracting Receipt", doc)
|
||||
for item in doc.supplied_items:
|
||||
account, cost_center = frappe.db.get_values(
|
||||
"Subcontracting Receipt Item", item.reference_name, ["expense_account", "cost_center"]
|
||||
)[0]
|
||||
|
||||
if not item.expense_account:
|
||||
item.db_set("expense_account", account)
|
||||
if not item.cost_center:
|
||||
item.db_set("cost_center", cost_center)
|
||||
|
||||
doc.make_gl_entries()
|
||||
|
||||
docs = frappe.get_all(
|
||||
"GL Entry",
|
||||
fields=["name", "voucher_no"],
|
||||
filters={"voucher_type": "Subcontracting Receipt", "account": ["is", "not set"], "is_cancelled": 0},
|
||||
pluck="voucher_no",
|
||||
)
|
||||
for doc in docs:
|
||||
doc = frappe.get_doc("Subcontracting Receipt", doc)
|
||||
for item in doc.supplied_items:
|
||||
account, cost_center = frappe.db.get_values(
|
||||
"Subcontracting Receipt Item", item.reference_name, ["expense_account", "cost_center"]
|
||||
)[0]
|
||||
|
||||
if not item.expense_account:
|
||||
item.db_set("expense_account", account)
|
||||
if not item.cost_center:
|
||||
item.db_set("cost_center", cost_center)
|
||||
|
||||
doc.docstatus = 2
|
||||
doc.make_gl_entries_on_cancel()
|
||||
doc.docstatus = 1
|
||||
doc.make_gl_entries()
|
||||
if docs:
|
||||
cancel_incorrect_gl_entries([d.name for d in docs])
|
||||
recreate_gl_entries([d.voucher_no for d in docs])
|
||||
|
||||
10
erpnext/patches/v15_0/update_uae_zero_rated_fetch.py
Normal file
10
erpnext/patches/v15_0/update_uae_zero_rated_fetch.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import frappe
|
||||
|
||||
from erpnext.regional.united_arab_emirates.setup import make_custom_fields
|
||||
|
||||
|
||||
def execute():
|
||||
if not frappe.db.get_value("Company", {"country": "United Arab Emirates"}):
|
||||
return
|
||||
|
||||
make_custom_fields()
|
||||
@@ -14,5 +14,6 @@ def get_data():
|
||||
{"label": _("Material"), "items": ["Material Request", "BOM", "Stock Entry"]},
|
||||
{"label": _("Sales"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
|
||||
{"label": _("Purchase"), "items": ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]},
|
||||
{"label": _("Manufacture"), "items": ["Work Order"]},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -928,15 +928,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
this.frm.refresh_fields();
|
||||
}
|
||||
|
||||
async set_default_payment(total_amount_to_pay, update_paid_amount) {
|
||||
set_default_payment(total_amount_to_pay, update_paid_amount) {
|
||||
var me = this;
|
||||
var payment_status = true;
|
||||
if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
|
||||
let r = await frappe.db.get_value("POS Profile", this.frm.doc.pos_profile, "disable_grand_total_to_default_mop");
|
||||
|
||||
if (r.message.disable_grand_total_to_default_mop) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(this.frm.doc['payments'] || [], function(index, data) {
|
||||
if(data.default && payment_status && total_amount_to_pay > 0) {
|
||||
|
||||
86
erpnext/regional/australia/setup.py
Normal file
86
erpnext/regional/australia/setup.py
Normal file
@@ -0,0 +1,86 @@
|
||||
import frappe
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import make_records
|
||||
|
||||
|
||||
def setup(company=None, patch=True):
|
||||
pass
|
||||
|
||||
|
||||
def update_regional_tax_settings(country=None, company=None):
|
||||
# tax rules
|
||||
records = [
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Purchase",
|
||||
"purchase_tax_template": frappe.db.get_value(
|
||||
"Purchase Taxes and Charges Template",
|
||||
{"title": "AU Capital Purchase - GST", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "Capital Goods Supplier",
|
||||
"priority": "10",
|
||||
"company": company,
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Purchase",
|
||||
"purchase_tax_template": frappe.db.get_value(
|
||||
"Purchase Taxes and Charges Template",
|
||||
{"title": "Import & GST-Free Purchase", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "Import / GST Free Supplier",
|
||||
"priority": "20",
|
||||
"company": company,
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Purchase",
|
||||
"purchase_tax_template": frappe.db.get_value(
|
||||
"Purchase Taxes and Charges Template",
|
||||
{"title": "AU Non Capital Purchase - GST", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "Domestic GST Supplier",
|
||||
"priority": "30",
|
||||
"company": company,
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Sales",
|
||||
"sales_tax_template": frappe.db.get_value(
|
||||
"Sales Taxes and Charges Template",
|
||||
{"title": "AU Sales - GST", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "Domestic GST Customer",
|
||||
"priority": "30",
|
||||
"company": company,
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Sales",
|
||||
"sales_tax_template": frappe.db.get_value(
|
||||
"Sales Taxes and Charges Template",
|
||||
{"title": "Export Sales - GST Free", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "Export Customer",
|
||||
"priority": "20",
|
||||
"company": company,
|
||||
},
|
||||
{
|
||||
"doctype": "Tax Rule",
|
||||
"tax_type": "Sales",
|
||||
"sales_tax_template": frappe.db.get_value(
|
||||
"Sales Taxes and Charges Template",
|
||||
{"title": "AU Sales - GST Free", "company": company},
|
||||
),
|
||||
"use_for_shopping_cart": "1",
|
||||
"tax_category": "GST Free Customer",
|
||||
"priority": "10",
|
||||
"company": company,
|
||||
},
|
||||
]
|
||||
|
||||
make_records(records)
|
||||
@@ -143,7 +143,7 @@ def get_total_emiratewise(filters):
|
||||
on
|
||||
i.parent = s.name
|
||||
where
|
||||
s.docstatus = 1 and i.is_exempt != 1 and i.is_zero_rated != 1
|
||||
s.docstatus = 1 and i.is_exempt != 1 and i.is_zero_rated != 1
|
||||
{conditions}
|
||||
group by
|
||||
s.vat_emirate;
|
||||
|
||||
@@ -20,6 +20,7 @@ def make_custom_fields():
|
||||
label="Is Zero Rated",
|
||||
fieldtype="Check",
|
||||
fetch_from="item_code.is_zero_rated",
|
||||
fetch_if_empty=1,
|
||||
insert_after="description",
|
||||
print_hide=1,
|
||||
)
|
||||
|
||||
@@ -7,10 +7,6 @@ from erpnext.controllers.taxes_and_totals import get_itemised_tax
|
||||
|
||||
|
||||
def update_itemised_tax_data(doc):
|
||||
# maybe this should be a standard function rather than a regional one
|
||||
if not doc.taxes:
|
||||
return
|
||||
|
||||
if not doc.items:
|
||||
return
|
||||
|
||||
@@ -20,6 +16,29 @@ def update_itemised_tax_data(doc):
|
||||
|
||||
itemised_tax = get_itemised_tax(doc.taxes)
|
||||
|
||||
def determine_if_export(doc):
|
||||
if doc.doctype != "Sales Invoice":
|
||||
return False
|
||||
|
||||
if not doc.customer_address:
|
||||
if not doc.total_taxes_and_charges:
|
||||
frappe.msgprint(
|
||||
_("Please set Customer Address to determine if the transaction is an export."),
|
||||
alert=True,
|
||||
)
|
||||
|
||||
return False
|
||||
|
||||
company_country = frappe.get_cached_value("Company", doc.company, "country")
|
||||
customer_country = frappe.db.get_value("Address", doc.customer_address, "country")
|
||||
|
||||
if company_country != customer_country:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
is_export = determine_if_export(doc)
|
||||
|
||||
for row in doc.items:
|
||||
tax_rate, tax_amount = 0.0, 0.0
|
||||
# dont even bother checking in item tax template as it contains both input and output accounts - double the tax rate
|
||||
@@ -30,6 +49,9 @@ def update_itemised_tax_data(doc):
|
||||
tax_amount += flt((row.net_amount * _tax_rate) / 100, row.precision("tax_amount"))
|
||||
tax_rate += _tax_rate
|
||||
|
||||
if not tax_rate or row.get("is_zero_rated"):
|
||||
row.is_zero_rated = is_export or frappe.get_cached_value("Item", row.item_code, "is_zero_rated")
|
||||
|
||||
row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
|
||||
row.tax_amount = flt(tax_amount, row.precision("tax_amount"))
|
||||
row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
|
||||
|
||||
@@ -1219,6 +1219,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
read_only: 1,
|
||||
fieldname: "uom",
|
||||
label: __("UOM"),
|
||||
options: "UOM",
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
@@ -1292,7 +1293,6 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
let pending_qty = (flt(d.stock_qty) - ordered_qty) / flt(d.conversion_factor);
|
||||
if (pending_qty > 0) {
|
||||
po_items.push({
|
||||
doctype: "Sales Order Item",
|
||||
name: d.name,
|
||||
item_name: d.item_name,
|
||||
item_code: d.item_code,
|
||||
|
||||
@@ -1785,7 +1785,9 @@ def create_pick_list(source_name, target_doc=None):
|
||||
|
||||
doc.purpose = "Delivery"
|
||||
|
||||
doc.set_item_locations()
|
||||
# Only auto-assign serial numbers if not picking manually
|
||||
if not doc.pick_manually:
|
||||
doc.set_item_locations()
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
@@ -421,6 +421,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
init_payments() {
|
||||
this.payment = new erpnext.PointOfSale.Payment({
|
||||
wrapper: this.$components_wrapper,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
get_frm: () => this.frm || {},
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
erpnext.PointOfSale.Payment = class {
|
||||
constructor({ events, wrapper }) {
|
||||
constructor({ events, settings, wrapper }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.disable_grand_total_to_default_mop = settings.disable_grand_total_to_default_mop;
|
||||
|
||||
this.init_component();
|
||||
}
|
||||
@@ -341,10 +342,11 @@ erpnext.PointOfSale.Payment = class {
|
||||
}
|
||||
|
||||
render_payment_section() {
|
||||
this.remove_grand_total_from_default_mop();
|
||||
this.render_payment_mode_dom();
|
||||
this.make_invoice_fields_control();
|
||||
this.update_totals_section();
|
||||
this.unset_grand_total_to_default_mop();
|
||||
this.focus_on_default_mop();
|
||||
}
|
||||
|
||||
after_render() {
|
||||
@@ -446,7 +448,19 @@ erpnext.PointOfSale.Payment = class {
|
||||
this.attach_cash_shortcuts(doc);
|
||||
}
|
||||
|
||||
remove_grand_total_from_default_mop() {
|
||||
if (!this.disable_grand_total_to_default_mop) return;
|
||||
const doc = this.events.get_frm().doc;
|
||||
const payments = doc.payments;
|
||||
payments.forEach((p) => {
|
||||
if (p.default) {
|
||||
frappe.model.set_value(p.doctype, p.name, "amount", 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
focus_on_default_mop() {
|
||||
if (this.disable_grand_total_to_default_mop) return;
|
||||
const doc = this.events.get_frm().doc;
|
||||
const payments = doc.payments;
|
||||
payments.forEach((p) => {
|
||||
@@ -629,19 +643,6 @@ erpnext.PointOfSale.Payment = class {
|
||||
.toLowerCase();
|
||||
}
|
||||
|
||||
async unset_grand_total_to_default_mop() {
|
||||
const doc = this.events.get_frm().doc;
|
||||
let r = await frappe.db.get_value(
|
||||
"POS Profile",
|
||||
doc.pos_profile,
|
||||
"disable_grand_total_to_default_mop"
|
||||
);
|
||||
|
||||
if (!r.message.disable_grand_total_to_default_mop) {
|
||||
this.focus_on_default_mop();
|
||||
}
|
||||
}
|
||||
|
||||
validate_reqd_invoice_fields() {
|
||||
const doc = this.events.get_frm().doc;
|
||||
let validation_flag = true;
|
||||
|
||||
@@ -35,7 +35,7 @@ def get_brand_defaults(item, company):
|
||||
|
||||
for d in brand.brand_defaults or []:
|
||||
if d.company == company:
|
||||
row = copy.deepcopy(d.as_dict())
|
||||
row = d.as_dict(no_private_properties=True)
|
||||
row.pop("name")
|
||||
return row
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ def get_item_group_defaults(item, company):
|
||||
|
||||
for d in item_group.item_group_defaults or []:
|
||||
if d.company == company:
|
||||
row = copy.deepcopy(d.as_dict())
|
||||
row = d.as_dict(no_private_properties=True)
|
||||
row.pop("name")
|
||||
return row
|
||||
|
||||
|
||||
@@ -26,13 +26,27 @@ class PartyType(Document):
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_party_type(doctype, txt, searchfield, start, page_len, filters):
|
||||
cond = ""
|
||||
account_type = None
|
||||
|
||||
if filters and filters.get("account"):
|
||||
account_type = frappe.db.get_value("Account", filters.get("account"), "account_type")
|
||||
cond = "and account_type = '%s'" % account_type
|
||||
if account_type:
|
||||
if account_type in ["Receivable", "Payable"]:
|
||||
# Include Employee regardless of its configured account_type, but still respect the text filter
|
||||
cond = "and (account_type = %(account_type)s or name = 'Employee')"
|
||||
else:
|
||||
cond = "and account_type = %(account_type)s"
|
||||
|
||||
return frappe.db.sql(
|
||||
# Build parameters dictionary
|
||||
params = {"txt": "%" + txt + "%", "start": start, "page_len": page_len}
|
||||
if account_type:
|
||||
params["account_type"] = account_type
|
||||
|
||||
result = frappe.db.sql(
|
||||
f"""select name from `tabParty Type`
|
||||
where `{searchfield}` LIKE %(txt)s {cond}
|
||||
order by name limit %(page_len)s offset %(start)s""",
|
||||
{"txt": "%" + txt + "%", "start": start, "page_len": page_len},
|
||||
where `{searchfield}` LIKE %(txt)s {cond}
|
||||
order by name limit %(page_len)s offset %(start)s""",
|
||||
params,
|
||||
)
|
||||
|
||||
return result or []
|
||||
|
||||
@@ -60,10 +60,155 @@
|
||||
},
|
||||
|
||||
"Australia": {
|
||||
"Australia GST": {
|
||||
"account_name": "GST 10%",
|
||||
"tax_rate": 10.00,
|
||||
"default": 1
|
||||
"tax_categories" :[
|
||||
{
|
||||
"title" : "Domestic GST Supplier"
|
||||
},
|
||||
{
|
||||
"title" : "Domestic GST Customer"
|
||||
},
|
||||
{
|
||||
"title" : "Export Customer"
|
||||
},
|
||||
{
|
||||
"title" : "GST Free Customer"
|
||||
},
|
||||
{
|
||||
"title" : "Capital Goods Supplier"
|
||||
},
|
||||
{
|
||||
"title" : "Import / GST Free Supplier"
|
||||
}
|
||||
],
|
||||
"chart_of_accounts": {
|
||||
"Australia - Chart of Accounts with Account Numbers": {
|
||||
"sales_tax_templates": [
|
||||
{
|
||||
"title": "AU Sales - GST",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Collected (Payable)",
|
||||
"account_number": "22010",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"is_default" : 1,
|
||||
"description": "GST Collected (Payable)",
|
||||
"rate": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Export Sales - GST Free",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Collected (Payable)",
|
||||
"account_number": "22010",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"description": "GST Collected (Payable)",
|
||||
"rate": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "AU Sales - GST Free",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Collected (Payable)",
|
||||
"account_number": "22010",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"description": "GST Collected (Payable)",
|
||||
"rate": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"purchase_tax_templates": [
|
||||
{
|
||||
"title": "AU Capital Purchase - GST",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Paid (Receivable)",
|
||||
"account_number": "22020",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"description": "GST Paid (Receivable)",
|
||||
"rate": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Import & GST-Free Purchase",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Paid (Receivable)",
|
||||
"account_number": "22020",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"description": "GST Paid (Receivable)",
|
||||
"rate": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "AU Non Capital Purchase - GST",
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": {
|
||||
"account_name": "GST Paid (Receivable)",
|
||||
"account_number": "22020",
|
||||
"account_type": "Tax",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"description": "GST Paid (Receivable)",
|
||||
"is_default" :1,
|
||||
"rate": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"item_tax_templates": [
|
||||
{
|
||||
"title": "GST Exempt Sales",
|
||||
"taxes": [
|
||||
{
|
||||
"tax_type": {
|
||||
"account_name": "GST Collected (Payable)",
|
||||
"account_number": "22010",
|
||||
"root_type": "Liability",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"tax_rate": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title" : "GST Exempt Purchase",
|
||||
"taxes": [
|
||||
{
|
||||
"tax_type": {
|
||||
"account_name": "GST Paid (Receivable)",
|
||||
"account_number": "22020",
|
||||
"root_type": "Liability",
|
||||
"tax_rate": "10"
|
||||
},
|
||||
"tax_rate": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -258,6 +258,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
@@ -1395,7 +1396,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-26 12:44:28.258215",
|
||||
"modified": "2025-08-04 19:20:47.724218",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
@@ -1486,6 +1487,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "status,customer,customer_name, territory,base_grand_total",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
@@ -1495,4 +1497,4 @@
|
||||
"title_field": "title",
|
||||
"track_changes": 1,
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ class DeliveryNote(SellingController):
|
||||
per_billed: DF.Percent
|
||||
per_installed: DF.Percent
|
||||
per_returned: DF.Percent
|
||||
pick_list: DF.Link | None
|
||||
plc_conversion_rate: DF.Float
|
||||
po_date: DF.Date | None
|
||||
po_no: DF.SmallText | None
|
||||
@@ -453,8 +452,6 @@ class DeliveryNote(SellingController):
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status()
|
||||
|
||||
self.update_stock_reservation_entries()
|
||||
|
||||
if not self.is_return:
|
||||
self.check_credit_limit()
|
||||
elif self.issue_credit_note:
|
||||
@@ -468,6 +465,7 @@ class DeliveryNote(SellingController):
|
||||
self.make_bundle_using_old_serial_batch_fields(table_name)
|
||||
|
||||
self.validate_standalone_serial_nos_customer()
|
||||
self.update_stock_reservation_entries()
|
||||
|
||||
# Updating stock ledger should always be called after updating prevdoc status,
|
||||
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||
|
||||
@@ -318,7 +318,8 @@
|
||||
"fieldname": "shelf_life_in_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Shelf Life In Days",
|
||||
"mandatory_depends_on": "eval:doc.has_batch_no && doc.has_expiry_date"
|
||||
"mandatory_depends_on": "eval:doc.has_batch_no && doc.has_expiry_date",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"default": "2099-12-31",
|
||||
@@ -362,7 +363,8 @@
|
||||
"depends_on": "is_stock_item",
|
||||
"fieldname": "weight_per_unit",
|
||||
"fieldtype": "Float",
|
||||
"label": "Weight Per Unit"
|
||||
"label": "Weight Per Unit",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_stock_item",
|
||||
@@ -534,13 +536,15 @@
|
||||
"fieldname": "min_order_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Minimum Order Qty",
|
||||
"non_negative": 1,
|
||||
"oldfieldname": "min_order_qty",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "safety_stock",
|
||||
"fieldtype": "Float",
|
||||
"label": "Safety Stock"
|
||||
"label": "Safety Stock",
|
||||
"non_negative": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_details_cb",
|
||||
@@ -551,6 +555,7 @@
|
||||
"fieldname": "lead_time_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Lead Time in days",
|
||||
"non_negative": 1,
|
||||
"oldfieldname": "lead_time_days",
|
||||
"oldfieldtype": "Int"
|
||||
},
|
||||
@@ -559,6 +564,7 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Last Purchase Rate",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"oldfieldname": "last_purchase_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"read_only": 1
|
||||
@@ -889,7 +895,7 @@
|
||||
"image_field": "image",
|
||||
"links": [],
|
||||
"make_attachments_public": 1,
|
||||
"modified": "2025-02-03 23:43:57.253667",
|
||||
"modified": "2025-08-08 14:58:48.674193",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
@@ -954,6 +960,7 @@
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "item_name,description,item_group,customer_code",
|
||||
"show_name_in_global_search": 1,
|
||||
"show_preview_popup": 1,
|
||||
|
||||
@@ -1281,7 +1281,7 @@ def get_item_defaults(item_code, company):
|
||||
|
||||
for d in item.item_defaults:
|
||||
if d.company == company:
|
||||
row = copy.deepcopy(d.as_dict())
|
||||
row = d.as_dict(no_private_properties=True)
|
||||
row.pop("name")
|
||||
out.update(row)
|
||||
return out
|
||||
|
||||
@@ -81,6 +81,7 @@ frappe.ui.form.on("Pick List", {
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
set_item_locations: (frm, save) => {
|
||||
if (!(frm.doc.locations && frm.doc.locations.length)) {
|
||||
frappe.msgprint(__("Add items in the Item Locations table"));
|
||||
@@ -101,11 +102,34 @@ frappe.ui.form.on("Pick List", {
|
||||
},
|
||||
|
||||
pick_manually: function (frm) {
|
||||
// Update warehouse field read-only property
|
||||
frm.fields_dict.locations.grid.update_docfield_property(
|
||||
"warehouse",
|
||||
"read_only",
|
||||
!frm.doc.pick_manually
|
||||
);
|
||||
|
||||
// Clear auto-assigned serial numbers and related fields when switching to manual picking
|
||||
if (frm.doc.pick_manually && frm.doc.locations) {
|
||||
let has_changes = false;
|
||||
frm.doc.locations.forEach((row) => {
|
||||
if (row.serial_no || row.batch_no || row.serial_and_batch_bundle) {
|
||||
row.serial_no = "";
|
||||
row.batch_no = "";
|
||||
row.serial_and_batch_bundle = "";
|
||||
row.picked_qty = 0;
|
||||
has_changes = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (has_changes) {
|
||||
frappe.show_alert(
|
||||
__("Cleared auto-assigned serial numbers and batch numbers for manual picking"),
|
||||
3
|
||||
);
|
||||
frm.refresh_field("locations");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get_item_locations: (frm) => {
|
||||
@@ -273,7 +297,7 @@ frappe.ui.form.on("Pick List", {
|
||||
max_qty_field: "qty",
|
||||
dont_allow_new_row: true,
|
||||
prompt_qty: frm.doc.prompt_qty,
|
||||
serial_no_field: "not_supported", // doesn't make sense for picklist without a separate field.
|
||||
serial_no_field: "serial_no",
|
||||
};
|
||||
const barcode_scanner = new erpnext.utils.BarcodeScanner(opts);
|
||||
barcode_scanner.process_scan();
|
||||
|
||||
@@ -572,10 +572,18 @@ class PickList(TransactionBase):
|
||||
|
||||
if not item.item_code:
|
||||
frappe.throw(f"Row #{item.idx}: Item Code is Mandatory")
|
||||
if not cint(
|
||||
frappe.get_cached_value("Item", item.item_code, "is_stock_item")
|
||||
) and not frappe.db.exists("Product Bundle", {"new_item_code": item.item_code, "disabled": 0}):
|
||||
continue
|
||||
|
||||
# Check if item is stock item or product bundle
|
||||
is_stock_item = cint(frappe.get_cached_value("Item", item.item_code, "is_stock_item"))
|
||||
is_product_bundle = frappe.db.exists(
|
||||
"Product Bundle", {"new_item_code": item.item_code, "disabled": 0}
|
||||
)
|
||||
|
||||
# Include non-stock items for delivery purposes, but skip them for warehouse assignment
|
||||
if not is_stock_item and not is_product_bundle:
|
||||
# For non-stock items, set warehouse to None and continue processing
|
||||
item.warehouse = None
|
||||
|
||||
item_code = item.item_code
|
||||
reference = item.sales_order_item or item.material_request_item
|
||||
key = (item_code, item.uom, item.warehouse, item.batch_no, reference)
|
||||
|
||||
@@ -243,6 +243,7 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
@@ -1083,7 +1084,8 @@
|
||||
"no_copy": 1,
|
||||
"options": "Delivery Note",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "scan_barcode",
|
||||
@@ -1290,7 +1292,7 @@
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-04-09 16:52:19.323878",
|
||||
"modified": "2025-08-06 16:41:02.690658",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
@@ -1360,4 +1362,4 @@
|
||||
"timeline_field": "supplier",
|
||||
"title_field": "title",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ class SerialandBatchBundle(Document):
|
||||
)
|
||||
|
||||
elif not frappe.db.exists("Stock Ledger Entry", {"voucher_detail_no": self.voucher_detail_no}):
|
||||
if self.voucher_type == "Delivery Note" and frappe.db.exists(
|
||||
if self.voucher_type in ["Delivery Note", "Sales Invoice"] and frappe.db.exists(
|
||||
"Packed Item", self.voucher_detail_no
|
||||
):
|
||||
return
|
||||
|
||||
@@ -218,6 +218,7 @@
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"label": "Posting Time",
|
||||
@@ -697,7 +698,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-08-13 19:05:42.386955",
|
||||
"modified": "2025-08-04 19:21:03.338958",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
@@ -763,6 +764,7 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "posting_date, from_warehouse, to_warehouse, purpose, remarks",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
@@ -770,4 +772,4 @@
|
||||
"states": [],
|
||||
"title_field": "stock_entry_type",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"default": "Now",
|
||||
"fieldname": "posting_time",
|
||||
"fieldtype": "Time",
|
||||
"in_list_view": 1,
|
||||
@@ -183,7 +184,7 @@
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-05-11 09:10:26.327652",
|
||||
"modified": "2025-08-04 19:21:20.179658",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reconciliation",
|
||||
@@ -203,9 +204,10 @@
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"search_fields": "posting_date",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,20 +567,15 @@ def get_item_warehouse(item, args, overwrite_warehouse, defaults=None):
|
||||
or args.get("warehouse")
|
||||
)
|
||||
|
||||
if not warehouse:
|
||||
defaults = frappe.defaults.get_defaults() or {}
|
||||
warehouse_exists = frappe.db.exists(
|
||||
"Warehouse", {"name": defaults.default_warehouse, "company": args.company}
|
||||
)
|
||||
if defaults.get("default_warehouse") and warehouse_exists:
|
||||
warehouse = defaults.default_warehouse
|
||||
|
||||
else:
|
||||
warehouse = args.get("warehouse")
|
||||
|
||||
if not warehouse:
|
||||
default_warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
|
||||
if frappe.db.get_value("Warehouse", default_warehouse, "company") == args.company:
|
||||
default_warehouse = frappe.get_single_value("Stock Settings", "default_warehouse")
|
||||
if (
|
||||
default_warehouse
|
||||
and frappe.get_cached_value("Warehouse", default_warehouse, "company") == args.company
|
||||
):
|
||||
return default_warehouse
|
||||
|
||||
return warehouse
|
||||
@@ -1287,12 +1282,12 @@ def get_conversion_factor(item_code, uom):
|
||||
|
||||
if variant_of:
|
||||
filters["parent"] = ("in", (item_code, variant_of))
|
||||
conversion_factor = frappe.db.get_value("UOM Conversion Detail", filters, "conversion_factor")
|
||||
conversion_factor = frappe.get_all("UOM Conversion Detail", filters, pluck="conversion_factor")
|
||||
if not conversion_factor:
|
||||
stock_uom = frappe.db.get_value("Item", item_code, "stock_uom")
|
||||
conversion_factor = get_uom_conv_factor(uom, stock_uom)
|
||||
conversion_factor = [get_uom_conv_factor(uom, stock_uom) or 1]
|
||||
|
||||
return {"conversion_factor": conversion_factor or 1.0}
|
||||
return {"conversion_factor": conversion_factor[-1]}
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -112,6 +112,7 @@ class SerialBatchBundle:
|
||||
"company": self.company,
|
||||
"is_rejected": self.is_rejected_entry(),
|
||||
"make_bundle_from_sle": 1,
|
||||
"sle": self.sle,
|
||||
}
|
||||
).make_serial_and_batch_bundle()
|
||||
|
||||
@@ -1004,6 +1005,14 @@ class SerialBatchCreation:
|
||||
|
||||
if not hasattr(self, "do_not_submit") or not self.do_not_submit:
|
||||
doc.flags.ignore_voucher_validation = True
|
||||
if self.get("sle"):
|
||||
doc.flags.ignore_validate = True
|
||||
doc.save()
|
||||
self.sle.db_set("serial_and_batch_bundle", doc.name, update_modified=False)
|
||||
|
||||
if doc.flags.ignore_validate:
|
||||
doc.flags.ignore_validate = False
|
||||
|
||||
doc.submit()
|
||||
else:
|
||||
doc.save()
|
||||
|
||||
Reference in New Issue
Block a user