Compare commits

..

92 Commits

Author SHA1 Message Date
mergify[bot]
8be179c860 chore: correct license text for GPLv3 (backport #32170) (#32173)
* chore: correct license text for GPLv3 (#32170)

[skip ci]
2022-09-12 14:01:29 +05:30
RJPvT
0b36799e8f fix: Exception handling in Plaid Integration
fix: Exception handling in Plaid Integration
2022-09-12 09:23:23 +05:30
Marica
1ee62a8342 Merge pull request #31280 from RJPvT/patch-2
fix: locale Currency and Float setting in update_employee
2022-06-09 18:29:56 +05:30
RJPvT
74748c03e2 fix: locale Currency and Float setting in update_employee
In fieldtypes locale settings (example NL) . and , changes whereby the field is inproperly filled
2022-06-08 10:55:15 +02:00
mergify[bot]
73484ef1bc fix(india): e-invoice eligibility if company gstin is not configured (#31276) 2022-06-08 12:55:16 +05:30
mergify[bot]
1c25d7c6f2 fix(india): error while parsing e-invoice (backport #31053) (#31224) 2022-06-06 14:28:40 +05:30
mergify[bot]
432210d13d fix(india): minor e-invoicing fixes (backport #30553) (#31067) 2022-05-19 14:26:38 +05:30
mergify[bot]
836bfc603a fix(stock_ledger): round off values near to zero (#30803)
(cherry picked from commit 6a014d12c1)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2022-04-26 10:16:06 +05:30
mergify[bot]
2d7c678a9d fix: warehouse naming when suffix is present (#30621) (#30768)
(cherry picked from commit be04eaf723)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-21 12:04:25 +05:30
Ankush Menat
a92c53dd2b chore: disable coverage
coverage adds overhead which isn't necessary on v12 branch now

[skip ci]
2022-04-21 11:41:08 +05:30
mergify[bot]
68fd8a1dcf fix(india): transporter name is null while generating e-way bill (backport #30736) 2022-04-20 19:48:22 +05:30
mergify[bot]
5b2b76c4a7 fix: dont fetch entire barcode table in get_item_details (#30131) (#30666)
(cherry picked from commit 64905188c4)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-10 11:52:35 +05:30
Saqib Ansari
c8780b0062 Merge pull request #30401 from nextchamp-saqib/add-client-cred-fields-einvoicing
feat: add client id & client secret fields in e invoice settings
2022-03-25 10:28:57 +05:30
Saqib Ansari
f531d1a69b feat: add client id & client secret fields in e invoice settings 2022-03-24 17:50:05 +05:30
Rucha Mahabal
51aa55f3b0 fix: salary slip amount rounding errors (#30250) 2022-03-15 12:23:48 +05:30
Saqib Ansari
db0e3d3fbe Merge pull request #30088 from frappe/mergify/bp/version-12-hotfix/pr-30084
fix(e-invoicing): remove batch no from e-invoices (backport #30084)
2022-03-07 11:57:29 +05:30
Saqib Ansari
ccffcf2596 fix(e-invoicing): remove batch no from e-invoices
(cherry picked from commit 031a0dd703)
2022-03-07 06:25:43 +00:00
Saqib Ansari
c74a6f1827 Merge pull request #29291 from GangaManoj/prevent-depr-of-sold-assets
fix: Display 'Make Depreciation Entry' only for submitted or partially depreciated Assets
2022-01-14 18:49:13 +05:30
GangaManoj
73243c8ac3 fix: Hide Make Depreciation Entry if the Asset is not in the Submitted or Partially Depreciated state 2022-01-14 17:40:59 +05:30
GangaManoj
1800ce2780 fix: Only allow posting of Depreciation Entries if the Asset is in the Submitted or Partially Depreciated state 2022-01-14 15:33:16 +05:30
Ankush Menat
e5ea16a6dd fix: loosen dependencies for pandas 2022-01-10 18:12:50 +05:30
rohitwaghchaure
753adf3ce4 Merge pull request #29063 from rohitwaghchaure/fixed-backport-issue
fix: travis failing
2021-12-29 23:27:46 +05:30
Rohit Waghchaure
fbbf29e829 fix: travis failing 2021-12-29 15:46:14 +05:30
Ganga Manoj
ce2aa767b2 fix: Use depreciation cost center for creating credit entries in JEs (#28908) 2021-12-23 12:04:15 +05:30
Ankush Menat
c9927efcae test: dynamic fiscal year creation in tests (#28667)
(cherry picked from commit fdffa037b5)

fix: resolve conflict

fix
2021-12-14 22:50:26 +05:30
Rucha Mahabal
e9dbb46a06 fix: incorrect amount based on payment days in timesheet salary slip (#28884) 2021-12-14 20:04:37 +05:30
mergify[bot]
39125a78e0 fix!: dont allow renaming warehouse primary key (backport #28712)
* fix!: dont allow renaming warehouse primary key

(cherry picked from commit 72dbc3d6b8)

* fix: remove autocommit from item rename

(cherry picked from commit 5caf411be3)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-12-14 17:45:08 +05:30
Saqib
2a00164059 Merge pull request #28848 from frappe/mergify/bp/version-12-hotfix/pr-28797
fix: validate if asset account is set against company (backport #28797)
2021-12-13 22:41:15 +05:30
Saqib
7d1953bb3b fix: validate if asset account is set against company
(cherry picked from commit c3e0de28b1)
2021-12-13 14:44:48 +00:00
Deepesh Garg
33eeb64fec Merge pull request #28463 from frappe/mergify/bp/version-12-hotfix/pr-28459
fix(India): GST category not getting auto updated (backport #28459)
2021-12-03 09:39:09 +05:30
mergify[bot]
abb4d99ca8 fix: actual tax conversion in case of multicurrency invoices (#28687) 2021-12-02 15:01:06 +05:30
Deepesh Garg
67f17c7a0c fix: Remove tests 2021-11-29 18:05:36 +05:30
Ankush Menat
24c004b537 fix: use get_all instead of get_list for child tables
(cherry picked from commit f862339024)
2021-11-29 16:28:04 +05:30
Ankush Menat
1fce25180c chore: correct docstrings
(cherry picked from commit c7701ace80)
2021-11-29 16:28:04 +05:30
Saqib
23946cea2e fix: debit & credit currency in bank transaction (#28574) 2021-11-29 15:13:22 +05:30
mergify[bot]
1b78c501a1 fix: incorrect balance for warehouses (#28583) (#28585)
(cherry picked from commit 7ff30a4b2b)

Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2021-11-26 17:36:16 +05:30
Deepesh Garg
d09ed0a578 fix: Conflicts 2021-11-19 13:11:07 +05:30
Deepesh Garg
9b83e3856a fix: Add test for gst category check
(cherry picked from commit cdbc991e3f)

# Conflicts:
#	erpnext/regional/doctype/gstr_3b_report/test_gstr_3b_report.py
2021-11-19 06:52:43 +00:00
Deepesh Garg
656686f2b1 fix(India): GST category not getting auto updated
(cherry picked from commit f8a26a9fac)
2021-11-19 06:52:42 +00:00
mergify[bot]
8e7ee953c1 chore: remove woocommerce package (#25736) (#28424)
This is not used anywhere. It was added in this commit
df83148d7c
even there it isn't being used.

(cherry picked from commit e1ab290911)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-11-17 09:50:18 +05:30
mergify[bot]
d2af2b31e5 fix: validate hmac unconditionally (#28375)
(cherry picked from commit c0f06bc8e3)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-12 14:53:51 +05:30
mergify[bot]
f79651c54d fix(WooCommerce): always expect signature in webhook requests (#28371)
(cherry picked from commit 24b048925b)

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2021-11-12 14:10:49 +05:30
mergify[bot]
80ba0cf978 fix: specify fields to be set in Lead (backport #28288)
* fix: specify fields to be set in Lead (#28288)

(cherry picked from commit da22744e0f)

# Conflicts:
#	erpnext/shopping_cart/cart.py

* fix: merge conflict

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2021-11-09 16:05:37 +05:30
Deepesh Garg
30c1a7bb20 Merge pull request #28279 from frappe/mergify/bp/version-12-hotfix/pr-28065
fix(minor): check if gst_category exists while validating GSTIN (backport #28065)
2021-11-09 13:48:29 +05:30
Anuja Pawar
d119b4321d fix: sider
(cherry picked from commit f2fbcc8124)
2021-11-08 18:37:03 +00:00
Anuja Pawar
4b1befb691 fix: update set_value query
(cherry picked from commit dd3cadd46b)
2021-11-08 18:37:01 +00:00
Anuja Pawar
fa5e018330 fix: re-writing sql query with ORM methods
(cherry picked from commit bc1e7bc15f)
2021-11-08 18:36:58 +00:00
Anuja Pawar
8c84d6a1d9 fix: sider
(cherry picked from commit 6a3bd882b4)
2021-11-08 18:36:56 +00:00
Anuja Pawar
f76139dceb fix: sider
(cherry picked from commit 4f53837624)
2021-11-08 18:36:54 +00:00
Anuja Pawar
97544e2bbc fix: check if gst_category exist
(cherry picked from commit 59c31bb124)
2021-11-08 18:36:53 +00:00
mergify[bot]
117718d410 fix: ignore unsupported methods while resyncing (#28210) (#28211)
(cherry picked from commit 72a050fb0b)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-02 23:09:44 +05:30
Marica
bcb0683be1 Merge pull request #28198 from frappe/mergify/bp/version-12-hotfix/pr-28195
fix: (ux) Remove warehouse filter on Batch field for Material Receipt (backport #28195)
2021-11-02 17:04:23 +05:30
marination
bf5f0a226e chore: Add comment above fix for future reference
(cherry picked from commit 48886ee705)
2021-11-02 10:41:16 +00:00
marination
55aaefb53c fix: Remove warehouse filter on Batch field for Material Receipt
(cherry picked from commit 048210a8f6)
2021-11-02 10:41:15 +00:00
Ankush Menat
ba537b0b93 fix: py2 string formatting 2021-10-29 15:41:55 +05:30
mergify[bot]
89b0e5023a fix: Skip empty rows while updating unsaved BOM cost (#28136) (#28141)
- Dont try to get valuation rate if row has no item code
- Dont try to add exploded items if row has no item code

(cherry picked from commit 292419bc9e)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2021-10-29 15:40:14 +05:30
Saqib
14602178e7 Merge pull request #28076 from GangaManoj/gross-profit-backport
feat: Make Gross Profit Report more readable
2021-10-27 15:07:23 +05:30
GangaManoj
5f2819c9b1 fix: Add column names 2021-10-25 02:24:19 +05:30
GangaManoj
5bc7d63646 fix: Get Bundle Items 2021-10-25 02:22:27 +05:30
GangaManoj
94162e0139 fix: Get Bundle Items 2021-10-25 02:15:56 +05:30
GangaManoj
e2e751e26f fix: Remove comments 2021-10-25 02:15:31 +05:30
GangaManoj
70244cee89 fix: Get data when grouped by invoice and otherwise 2021-10-25 02:15:03 +05:30
GangaManoj
ae6e69ebd4 fix: Display only the Invoice rows in bold 2021-10-25 02:14:32 +05:30
GangaManoj
6fdf4dd03e fix: Remove base_rate and buying_rate for Invoice rows 2021-10-25 02:12:21 +05:30
GangaManoj
4aede0ea3f fix: Calculate total buying_amount for each invoice 2021-10-25 02:11:14 +05:30
GangaManoj
de660bf9c4 fix: Fetch bundle item details 2021-10-25 02:11:10 +05:30
GangaManoj
391bf86e0a fix: Fetch base_net_total for each Invoice 2021-10-25 02:10:43 +05:30
GangaManoj
452c613974 fix: Calculate total buying_amount and gross profit for each invoice 2021-10-25 02:08:40 +05:30
GangaManoj
dd7b02ad7d fix: Remove Item Code and Item Name columns 2021-10-25 02:08:39 +05:30
GangaManoj
7cc02bf861 fix: Display Items in the format Item Code: Item Name 2021-10-25 02:08:36 +05:30
GangaManoj
678960209c fix: Add items belonging to Product Bundles as children 2021-10-25 02:08:12 +05:30
GangaManoj
e4995dc9ed fix: Set initial_depth to 3 2021-10-25 02:08:12 +05:30
GangaManoj
7cf5dc7dab fix: Assign indent and parent_invoice 2021-10-25 02:08:08 +05:30
GangaManoj
e676a09c18 fix: Make Invoice row bold 2021-10-25 02:04:03 +05:30
GangaManoj
6431243ce3 fix: Display items as descendants of invoices 2021-10-25 02:04:00 +05:30
GangaManoj
49f93b347c fix: Display data in tree form 2021-10-25 02:02:02 +05:30
mergify[bot]
27e9e47ba8 chore: warning for shopify integration deprecation (backport #26701) (#28062)
* chore: warning for shopify integration deprecation (#26701)

* chore: warning for shopify integration deprecation

* fix: warn deprecation during patch for sysadmins

(cherry picked from commit e43bdf76a5)

# Conflicts:
#	erpnext/patches.txt

* fix: resolve conflicts

Co-authored-by: Ankush <ankush@iwebnotes.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-10-22 12:16:55 +05:30
Jannat Patel
c9f51d3cec Merge pull request #28017 from frappe/mergify/bp/version-12-hotfix/pr-27728
fix: Employee Leave Balance report should only consider ledgers of transaction type Leave Allocation (backport #27728)
2021-10-20 14:57:52 +05:30
Jannat Patel
9ae3f26dbf fix: conflicts 2021-10-20 12:34:04 +05:30
Goh Yan Chang
ad736f1789 Update employee_leave_balance.py
fix: Employee Leave Balance report showing wrong figures
(cherry picked from commit 632f7848a3)

# Conflicts:
#	erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
2021-10-20 06:58:37 +00:00
Ankush Menat
16ece12516 fix: py2 compatible string formatting 2021-10-09 18:12:07 +05:30
Gavin D'souza
c9e75d2ab5 fix(patch): Handle NULL values from fieldtype change 2021-10-09 18:08:46 +05:30
mergify[bot]
985fdade7e fix(asset): expected value after useful life validation (#27791) 2021-10-05 14:14:52 +05:30
Saqib
10c34da174 fix: unexpected keyword argument 'as_dict' (#27799) 2021-10-05 13:13:20 +05:30
mergify[bot]
d1480be596 fix: set item uom as stock_uom if it isn't set (backport #27623) (#27781)
* fix: set item uom as stock_uom if it isn't set (#27623)

* fix: set item uom as stock_uom if it isn't set

(cherry picked from commit 5c372202d5)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.js

* fix: resolve conflicts

[skip ci]

Co-authored-by: Alan <2.alan.tom@gmail.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-10-04 22:50:11 +05:30
Noah Jacob
93a744dc12 fix: empty row on new maintenance visit. (#27626)
* fix: empty row on new maintenance visit.

* fix: check if rows exist
2021-09-30 15:40:37 +05:30
Saqib
a2b98bb80c fix: cannot delete a project if linked with sales order (#27690) 2021-09-28 19:47:08 +05:30
Frappe PR Bot
4fdff12242 fix: set item.qty as mandatory (#27681)
* fix: set item.qty as mandatory in picklist (#27680)

(cherry picked from commit b91333afdd)

# Conflicts:
#	erpnext/stock/doctype/pick_list_item/pick_list_item.json

* fix: resolve conflicts

Co-authored-by: Alan <2.alan.tom@gmail.com>
2021-09-28 07:48:37 +00:00
Jannat Patel
9c9cde48ad Merge pull request #27679 from pateljannat/revert-27037-salary-component-account-filter
Revert "fix: salary component account filter"
2021-09-28 11:44:34 +05:30
pateljannat
999a3f1305 Revert 'fix: salary component account filter' 2021-09-28 10:32:33 +05:30
mergify[bot]
8139672c7a fix: Validate if item exists on uploading items in stock reco (#27538) (#27593)
* fix: Validate if item exists on uploading items in stock reco

- Uploading non existent item in stock reco and then changing warehouse or batch gave an error
- Check for non existent item

* chore: translation

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2021-09-20 18:19:14 +05:30
Frappe PR Bot
7169a4c113 fix(plaid): query to check if bank account exists (#27594) 2021-09-20 16:41:26 +05:30
54 changed files with 865 additions and 678 deletions

View File

@@ -16,11 +16,11 @@ jobs:
include:
- name: "Python 2.7 Server Side Test"
python: 2.7
script: bench --site test_site run-tests --app erpnext --coverage
script: bench --site test_site run-tests --app erpnext
- name: "Python 3.6 Server Side Test"
python: 3.6
script: bench --site test_site run-tests --app erpnext --coverage
script: bench --site test_site run-tests --app erpnext
- name: "Python 2.7 Patch Test"
python: 2.7
@@ -74,8 +74,3 @@ install:
- bench get-app erpnext $TRAVIS_BUILD_DIR
- bench start &
- bench --site test_site reinstall --yes
after_script:
- pip install coverage==4.5.4
- pip install python-coveralls
- coveralls -b apps/erpnext -d ../../sites/.coverage

View File

@@ -44,6 +44,8 @@ GNU/General Public License (see [license.txt](license.txt))
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
By contributing to ERPNext, you agree that your contributions will be licensed under its GNU General Public License (v3).
---
## Contributing

View File

@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '12.25.0'
__version__ = '12.23.0'
def get_default_company(user=None):
'''Get default company for user'''

View File

@@ -271,6 +271,7 @@
"label": "Debit",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -304,6 +305,7 @@
"label": "Credit",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -632,6 +634,7 @@
"label": "Allocated Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -731,6 +734,7 @@
"label": "Unallocated Amount",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -755,7 +759,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-05-11 05:27:55.244721",
"modified": "2021-11-26 12:44:55.244721",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",

View File

@@ -3,11 +3,13 @@
from __future__ import unicode_literals
import frappe, unittest
import unittest
import frappe
from frappe.utils import now_datetime
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
test_records = frappe.get_test_records('Fiscal Year')
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
@@ -23,3 +25,29 @@ class TestFiscalYear(unittest.TestCase):
})
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
def test_record_generator():
test_records = [
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31"
}
]
start = 2012
end = now_datetime().year + 5
for year in range(start, end):
test_records.append({
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year {}".format(year),
"year_start_date": "{}-01-01".format(year),
"year_end_date": "{}-12-31".format(year)
})
return test_records
test_records = test_record_generator()

View File

@@ -1,69 +0,0 @@
[
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2012",
"year_end_date": "2012-12-31",
"year_start_date": "2012-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2013",
"year_end_date": "2013-12-31",
"year_start_date": "2013-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2014",
"year_end_date": "2014-12-31",
"year_start_date": "2014-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2015",
"year_end_date": "2015-12-31",
"year_start_date": "2015-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2016",
"year_end_date": "2016-12-31",
"year_start_date": "2016-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2017",
"year_end_date": "2017-12-31",
"year_start_date": "2017-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2018",
"year_end_date": "2018-12-31",
"year_start_date": "2018-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2019",
"year_end_date": "2019-12-31",
"year_start_date": "2019-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2020",
"year_end_date": "2020-12-31",
"year_start_date": "2020-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2021",
"year_end_date": "2021-12-31",
"year_start_date": "2021-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2021",
"is_short_year": 1,
"year_end_date": "2021-12-31",
"year_start_date": "2021-04-01"
}
]

View File

@@ -247,8 +247,15 @@ class PurchaseInvoice(BuyingController):
else:
item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
asset_category_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
company = self.company)
if not asset_category_account:
form_link = get_link_to_form('Asset Category', asset_category)
throw(
_("Please set Fixed Asset Account in {} against {}.").format(form_link, self.company),
title=_("Missing Account")
)
item.expense_account = asset_category_account
elif item.is_fixed_asset and item.pr_detail:
item.expense_account = asset_received_but_not_billed
elif not item.expense_account and for_validate:

View File

@@ -1,7 +1,8 @@
{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%}
{%- set einvoice = json.loads(doc.signed_einvoice) -%}
<div class="page-break">
{% if doc.signed_einvoice %}
{%- set einvoice = json.loads(doc.signed_einvoice) -%}
<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
{% if letter_head and not no_letterhead %}
<div class="letter-head">{{ letter_head }}</div>
@@ -163,4 +164,10 @@
</tbody>
</table>
</div>
</div>
{% else %}
<div class="text-center" style="color: #98A1A9; font-size: 14px;">
You must generate IRN before you can preview GST E-Invoice.
</div>
{% endif %}
</div>

View File

@@ -36,5 +36,20 @@ frappe.query_reports["Gross Profit"] = {
"options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject",
"default": "Invoice"
},
]
],
"tree": true,
"name_field": "parent",
"parent_field": "parent_invoice",
"initial_depth": 3,
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (data && data.indent == 0.0) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");
value = $value.wrap("<p></p>").parent().html();
}
return value;
},
}

View File

@@ -41,6 +41,34 @@ def execute(filters=None):
columns = get_columns(group_wise_columns, filters)
if filters.group_by == 'Invoice':
get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data)
else:
get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data)
return columns, data
def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_wise_columns, data):
column_names = get_column_names()
# to display item as Item Code: Item Name
columns[0] = 'Sales Invoice:Link/Item:300'
# removing Item Code and Item Name columns
del columns[4:6]
for src in gross_profit_data.si_list:
row = frappe._dict()
row.indent = src.indent
row.parent_invoice = src.parent_invoice
row.currency = filters.currency
for col in group_wise_columns.get(scrub(filters.group_by)):
row[column_names[col]] = src.get(col)
data.append(row)
def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data):
for idx, src in enumerate(gross_profit_data.grouped_data):
row = []
for col in group_wise_columns.get(scrub(filters.group_by)):
@@ -51,8 +79,6 @@ def execute(filters=None):
row[0] = frappe.bold("Total")
data.append(row)
return columns, data
def get_columns(group_wise_columns, filters):
columns = []
column_map = frappe._dict({
@@ -93,12 +119,38 @@ def get_columns(group_wise_columns, filters):
return columns
def get_column_names():
return frappe._dict({
'parent': 'sales_invoice',
'customer': 'customer',
'customer_group': 'customer_group',
'posting_date': 'posting_date',
'item_code': 'item_code',
'item_name': 'item_name',
'item_group': 'item_group',
'brand': 'brand',
'description': 'description',
'warehouse': 'warehouse',
'qty': 'qty',
'base_rate': 'avg._selling_rate',
'buying_rate': 'valuation_rate',
'base_amount': 'selling_amount',
'buying_amount': 'buying_amount',
'gross_profit': 'gross_profit',
'gross_profit_percent': 'gross_profit_%',
'project': 'project'
})
class GrossProfitGenerator(object):
def __init__(self, filters=None):
self.data = []
self.average_buying_rate = {}
self.filters = frappe._dict(filters)
self.load_invoice_items()
if filters.group_by == 'Invoice':
self.group_items_by_invoice()
self.load_stock_ledger_entries()
self.load_product_bundle()
self.load_non_stock_items()
@@ -112,7 +164,12 @@ class GrossProfitGenerator(object):
self.currency_precision = cint(frappe.db.get_default("currency_precision")) or 3
self.float_precision = cint(frappe.db.get_default("float_precision")) or 2
for row in self.si_list:
grouped_by_invoice = True if self.filters.get("group_by") == "Invoice" else False
if grouped_by_invoice:
buying_amount = 0
for row in reversed(self.si_list):
if self.skip_row(row, self.product_bundles):
continue
@@ -134,12 +191,20 @@ class GrossProfitGenerator(object):
row.buying_amount = flt(self.get_buying_amount(row, row.item_code),
self.currency_precision)
if grouped_by_invoice:
if row.indent == 1.0:
buying_amount += row.buying_amount
elif row.indent == 0.0:
row.buying_amount = buying_amount
buying_amount = 0
# get buying rate
if row.qty:
row.buying_rate = flt(row.buying_amount / row.qty, self.float_precision)
row.base_rate = flt(row.base_amount / row.qty, self.float_precision)
else:
row.buying_rate, row.base_rate = 0.0, 0.0
if self.is_not_invoice_row(row):
row.buying_rate, row.base_rate = 0.0, 0.0
# calculate gross profit
row.gross_profit = flt(row.base_amount - row.buying_amount, self.currency_precision)
@@ -185,14 +250,17 @@ class GrossProfitGenerator(object):
for returned_item_row in returned_item_rows:
row.qty += returned_item_row.qty
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
if row.qty or row.base_amount:
row.buying_amount = flt(flt(row.qty) * flt(row.buying_rate), self.currency_precision)
if (flt(row.qty) or row.base_amount) and self.is_not_invoice_row(row):
row = self.set_average_rate(row)
self.grouped_data.append(row)
self.add_to_totals(row)
self.set_average_gross_profit(self.totals)
self.grouped_data.append(self.totals)
def is_not_invoice_row(self, row):
return (self.filters.get("group_by") == "Invoice" and row.indent != 0.0) or self.filters.get("group_by") != "Invoice"
def set_average_rate(self, new_row):
self.set_average_gross_profit(new_row)
new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
@@ -356,6 +424,109 @@ class GrossProfitGenerator(object):
.format(conditions=conditions, sales_person_cols=sales_person_cols,
sales_team_table=sales_team_table, match_cond = get_match_cond('Sales Invoice')), self.filters, as_dict=1)
def group_items_by_invoice(self):
"""
Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
"""
parents = []
for row in self.si_list:
if row.parent not in parents:
parents.append(row.parent)
parents_index = 0
for index, row in enumerate(self.si_list):
if parents_index < len(parents) and row.parent == parents[parents_index]:
invoice = self.get_invoice_row(row)
self.si_list.insert(index, invoice)
parents_index += 1
else:
# skipping the bundle items rows
if not row.indent:
row.indent = 1.0
row.parent_invoice = row.parent
row.parent = row.item_code
if frappe.db.exists('Product Bundle', row.item_code):
self.add_bundle_items(row, index)
def get_invoice_row(self, row):
return frappe._dict({
'parent_invoice': "",
'indent': 0.0,
'parent': row.parent,
'posting_date': row.posting_date,
'posting_time': row.posting_time,
'project': row.project,
'update_stock': row.update_stock,
'customer': row.customer,
'customer_group': row.customer_group,
'item_code': None,
'item_name': None,
'description': None,
'warehouse': None,
'item_group': None,
'brand': None,
'dn_detail': None,
'delivery_note': None,
'qty': None,
'item_row': None,
'is_return': row.is_return,
'cost_center': row.cost_center,
'base_net_amount': frappe.db.get_value('Sales Invoice', row.parent, 'base_net_total')
})
def add_bundle_items(self, product_bundle, index):
bundle_items = self.get_bundle_items(product_bundle)
for i, item in enumerate(bundle_items):
bundle_item = self.get_bundle_item_row(product_bundle, item)
self.si_list.insert((index+i+1), bundle_item)
def get_bundle_items(self, product_bundle):
return frappe.get_all(
'Product Bundle Item',
filters = {
'parent': product_bundle.item_code
},
fields = ['item_code', 'qty']
)
def get_bundle_item_row(self, product_bundle, item):
item_name, description, item_group, brand = self.get_bundle_item_details(item.item_code)
return frappe._dict({
'parent_invoice': product_bundle.item_code,
'indent': product_bundle.indent + 1,
'parent': item.item_code,
'posting_date': product_bundle.posting_date,
'posting_time': product_bundle.posting_time,
'project': product_bundle.project,
'customer': product_bundle.customer,
'customer_group': product_bundle.customer_group,
'item_code': item.item_code,
'item_name': item_name,
'description': description,
'warehouse': product_bundle.warehouse,
'item_group': item_group,
'brand': brand,
'dn_detail': product_bundle.dn_detail,
'delivery_note': product_bundle.delivery_note,
'qty': (flt(product_bundle.qty) * flt(item.qty)),
'item_row': None,
'is_return': product_bundle.is_return,
'cost_center': product_bundle.cost_center
})
def get_bundle_item_details(self, item_code):
return frappe.db.get_value(
'Item',
item_code,
['item_name', 'description', 'item_group', 'brand']
)
def load_stock_ledger_entries(self):
res = frappe.db.sql("""select item_code, voucher_type, voucher_no,
voucher_detail_no, stock_value, warehouse, actual_qty as qty

View File

@@ -78,6 +78,7 @@ frappe.ui.form.on('Asset', {
frappe.ui.form.trigger("Asset", "is_existing_asset");
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
frm.events.make_schedules_editable(frm);
frm.trigger("toggle_make_depreciation_entry");
if (frm.doc.docstatus==1) {
if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
@@ -141,6 +142,18 @@ frappe.ui.form.on('Asset', {
}
},
toggle_make_depreciation_entry: function(frm) {
if (frm.doc.calculate_depreciation){
if (in_list(["Submitted", "Partially Depreciated"], frm.doc.status)){
frm.fields_dict['schedules'].grid.set_column_disp('make_depreciation_entry', true);
} else {
frm.fields_dict['schedules'].grid.set_column_disp('make_depreciation_entry', false);
}
frm.refresh_field('schedules');
}
},
toggle_reference_doc: function(frm) {
if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) {
frm.set_df_property('purchase_invoice', 'read_only', 1);

View File

@@ -122,11 +122,6 @@ class Asset(AccountsController):
if self.is_existing_asset:
return
docname = self.purchase_receipt or self.purchase_invoice
if docname:
doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
date = frappe.db.get_value(doctype, docname, 'posting_date')
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
frappe.throw(_("Available-for-use Date should be after purchase date"))
@@ -404,9 +399,10 @@ class Asset(AccountsController):
if accumulated_depreciation_after_full_schedule:
accumulated_depreciation_after_full_schedule = max(accumulated_depreciation_after_full_schedule)
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
flt(accumulated_depreciation_after_full_schedule),
self.precision('gross_purchase_amount'))
asset_value_after_full_schedule = flt(
flt(self.gross_purchase_amount) -
flt(self.opening_accumulated_depreciation) -
flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount'))
if (row.expected_value_after_useful_life and
row.expected_value_after_useful_life < asset_value_after_full_schedule):

View File

@@ -34,6 +34,8 @@ def make_depreciation_entry(asset_name, date=None):
date = today()
asset = frappe.get_doc("Asset", asset_name)
validate_asset(asset)
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
get_depreciation_accounts(asset)
@@ -59,7 +61,7 @@ def make_depreciation_entry(asset_name, date=None):
"credit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name,
"cost_center": ""
"cost_center": depreciation_cost_center
}
debit_entry = {
@@ -101,6 +103,10 @@ def make_depreciation_entry(asset_name, date=None):
return asset
def validate_asset(asset):
if asset.status not in ['Submitted', 'Partially Depreciated']:
frappe.throw(_("Cannot depreciate {0} Asset").format(asset.status))
def get_depreciation_accounts(asset):
fixed_asset_account = accumulated_depreciation_account = depreciation_expense_account = None

View File

@@ -1,13 +0,0 @@
## Version 12.24.0 Release Notes
### Fixes & Enhancements
- Sales order qty update fails in "Update Items" button ([#26992](https://github.com/frappe/erpnext/pull/26992))
- Speed up validate_account_for_perpetual_inventory ([#26730](https://github.com/frappe/erpnext/pull/26730))
- Deadlock while doing payment reconciliation ([#26674](https://github.com/frappe/erpnext/pull/26674))
- Remove incorrect condition in GLE comparison ([#26713](https://github.com/frappe/erpnext/pull/26713))
- Cannot cancel invoice if IRN cancelled on portal ([#26880](https://github.com/frappe/erpnext/pull/26880))
- Item name is missing into job card ([#26956](https://github.com/frappe/erpnext/pull/26956))
- Removed company filter for Loan Type ([#26463](https://github.com/frappe/erpnext/pull/26463))
- Exchange rate revaluation posting date and precision fixes ([#26629](https://github.com/frappe/erpnext/pull/26629))
- Fixed clearing issue of payment references on setting cost center ([#26548](https://github.com/frappe/erpnext/pull/26548))
- Increase number of supported currency exchanges ([#25722](https://github.com/frappe/erpnext/pull/25722))

View File

@@ -1,10 +0,0 @@
## Version 12.25.0 Release Notes
### Fixes & Enhancements
- Multiple price rules margin. ([#24844](https://github.com/frappe/erpnext/pull/24844))
- Document naming rule not working for subscription invoices ([#27394](https://github.com/frappe/erpnext/pull/27394))
- Prematurely referenced variable in buying controller for subcontracting ([#27333](https://github.com/frappe/erpnext/pull/27333))
- Calculation of gross profit percentage in Gross Profit Report ([#26713](https://github.com/frappe/erpnext/pull/27045))
- Price list rate not fetched for return sales invoice fixed ([#26593](https://github.com/frappe/erpnext/pull/26593))
- Set production plan to completed even on over production ([#27027](https://github.com/frappe/erpnext/pull/27027))
- Add `total_billing_hours` to Sales Invoice ([#26652](https://github.com/frappe/erpnext/pull/26652))

View File

@@ -14,8 +14,7 @@ def verify_request():
)
if frappe.request.data and \
frappe.get_request_header("X-Wc-Webhook-Signature") and \
not sig == bytes(frappe.get_request_header("X-Wc-Webhook-Signature").encode()):
not sig == frappe.get_request_header("X-Wc-Webhook-Signature", "").encode():
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(woocommerce_settings.creation_user)

View File

@@ -83,10 +83,8 @@ def add_bank_accounts(response, bank, company):
if not acc_subtype:
add_account_subtype(account["subtype"])
existing_bank_account = frappe.db.exists("Bank Account", {
'account_name': account["name"],
'bank': bank["bank_name"]
})
bank_account_name = "{} - {}".format(account["name"], bank["bank_name"])
existing_bank_account = frappe.db.exists("Bank Account", bank_account_name)
if not existing_bank_account:
try:
@@ -198,6 +196,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
plaid = PlaidConnector(access_token)
transactions = []
try:
transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
except ItemError as e:
@@ -206,7 +205,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
return transactions or []
return transactions
def new_bank_transaction(transaction):
@@ -227,7 +226,7 @@ def new_bank_transaction(transaction):
try:
tags += transaction["category"]
tags += ["Plaid Cat. {}".format(transaction["category_id"])]
except KeyError:
except (KeyError, TypeError):
pass
if not frappe.db.exists("Bank Transaction", dict(transaction_id=transaction["transaction_id"])):
@@ -274,4 +273,4 @@ def automatic_synchronization():
@frappe.whitelist()
def get_link_token_for_update(access_token):
plaid = PlaidConnector(access_token)
return plaid.get_link_token(update_mode=True)
return plaid.get_link_token(update_mode=True)

View File

@@ -18,5 +18,8 @@ frappe.ui.form.on('Shopify Log', {
})
}).addClass('btn-primary');
}
let app_link = "<a href='https://frappecloud.com/marketplace/apps/ecommerce-integrations' target='_blank'>Ecommerce Integrations</a>"
frm.dashboard.add_comment(__("Shopify Integration will be removed from ERPNext in Version 14. Please install {0} app to continue using it.", [app_link]), "yellow", true);
}
});

View File

@@ -64,5 +64,8 @@ def dump_request_data(data, event="create/order"):
@frappe.whitelist()
def resync(method, name, request_data):
frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False)
if not method.startswith("erpnext.erpnext_integrations.connectors.shopify_connection"):
return
frappe.enqueue(method=method, queue='short', timeout=300, is_async=True,
**{"order": json.loads(request_data), "request_id": name})

View File

@@ -36,6 +36,10 @@ frappe.ui.form.on("Shopify Settings", "refresh", function(frm){
frm.toggle_reqd("delivery_note_series", frm.doc.sync_delivery_note);
}
let app_link = "<a href='https://frappecloud.com/marketplace/apps/ecommerce-integrations' target='_blank'>Ecommerce Integrations</a>"
frm.dashboard.add_comment(__("Shopify Integration will be removed from ERPNext in Version 14. Please install {0} app to continue using it.", [app_link]), "yellow", true);
})
$.extend(erpnext_integrations.shopify_settings, {

View File

@@ -18,7 +18,6 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'):
)
if frappe.request.data and \
frappe.get_request_header(hmac_key) and \
not sig == bytes(frappe.get_request_header(hmac_key).encode()):
frappe.throw(_("Unverified Webhook Data"))
frappe.set_user(settings.modified_by)

View File

@@ -6,16 +6,10 @@ frappe.ui.form.on('Salary Component', {
frm.set_query("default_account", "accounts", function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
var root_type = "Liability";
if (frm.doc.type == "Deduction") {
root_type = "Expense";
}
return {
filters: {
"is_group": 0,
"company": d.company,
"root_type": root_type
"company": d.company
}
};
});

View File

@@ -303,11 +303,11 @@ class SalarySlip(TransactionBase):
if self.salary_structure:
self.calculate_component_amounts("deductions")
self.set_loan_repayment()
self.set_component_amounts_based_on_payment_days()
self.set_net_pay()
def set_net_pay(self):
self.total_deduction = self.get_component_totals("deductions")
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
@@ -519,7 +519,7 @@ class SalarySlip(TransactionBase):
# Total taxable earnings including additional and other incomes
total_taxable_earnings = previous_taxable_earnings + current_structured_taxable_earnings + future_structured_taxable_earnings \
+ current_additional_earnings + other_incomes + unclaimed_taxable_benefits - total_exemption_amount
# Total taxable earnings without additional earnings with full tax
total_taxable_earnings_without_full_tax_addl_components = total_taxable_earnings - current_additional_earnings_with_full_tax
@@ -527,7 +527,7 @@ class SalarySlip(TransactionBase):
total_structured_tax_amount = self.calculate_tax_by_tax_slab(
total_taxable_earnings_without_full_tax_addl_components, tax_slab)
current_structured_tax_amount = (total_structured_tax_amount - previous_total_paid_taxes) / remaining_sub_periods
# Total taxable earnings with additional earnings with full tax
full_tax_on_additional_earnings = 0.0
if current_additional_earnings_with_full_tax:
@@ -563,7 +563,7 @@ class SalarySlip(TransactionBase):
select sum(sd.amount)
from
`tabSalary Detail` sd join `tabSalary Slip` ss on sd.parent=ss.name
where
where
sd.parentfield='earnings'
and sd.is_tax_applicable=1
and is_flexible_benefit=0
@@ -676,9 +676,11 @@ class SalarySlip(TransactionBase):
def get_amount_based_on_payment_days(self, row, joining_date, relieving_date):
amount, additional_amount = row.amount, row.additional_amount
timesheet_component = frappe.db.get_value("Salary Structure", self.salary_structure, "salary_component")
if (self.salary_structure and
cint(row.depends_on_payment_days) and cint(self.total_working_days) and
(not self.salary_slip_based_on_timesheet or
cint(row.depends_on_payment_days) and cint(self.total_working_days)
and (row.salary_component != timesheet_component or
getdate(self.start_date) < joining_date or
(relieving_date and getdate(self.end_date) > relieving_date)
)):
@@ -687,14 +689,14 @@ class SalarySlip(TransactionBase):
amount = flt((flt(row.default_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), row.precision("amount")) + additional_amount
elif not self.payment_days and not self.salary_slip_based_on_timesheet and cint(row.depends_on_payment_days):
elif not self.payment_days and row.salary_component != timesheet_component and cint(row.depends_on_payment_days):
amount, additional_amount = 0, 0
elif not row.amount:
amount = flt(row.default_amount) + flt(row.additional_amount)
# apply rounding
if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
amount, additional_amount = rounded(amount), rounded(additional_amount)
amount, additional_amount = rounded(amount or 0), rounded(additional_amount or 0)
return amount, additional_amount
@@ -782,7 +784,7 @@ class SalarySlip(TransactionBase):
if flt(d.max_taxable_income) and flt(d.max_taxable_income) < annual_taxable_earning:
continue
tax_amount += tax_amount * flt(d.percent) / 100
return tax_amount

View File

@@ -115,6 +115,41 @@ class TestSalarySlip(unittest.TestCase):
frappe.db.set_value("Employee", frappe.get_value("Employee",
{"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
def test_payment_days_in_salary_slip_based_on_timesheet(self):
from erpnext.projects.doctype.timesheet.test_timesheet import (
make_salary_structure_for_timesheet,
make_timesheet,
)
from erpnext.projects.doctype.timesheet.timesheet import (
make_salary_slip as make_salary_slip_for_timesheet,
)
# Holidays included in working days
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
emp = make_employee("test_employee_timesheet1@salary.com", company=erpnext.get_default_company())
frappe.db.set_value("Employee", emp, {"relieving_date": None, "status": "Active"})
# salary structure based on timesheet
make_salary_structure_for_timesheet(emp)
timesheet = make_timesheet(emp, simulate=True)
salary_slip = make_salary_slip_for_timesheet(timesheet.name)
salary_slip.start_date = get_first_day(nowdate())
salary_slip.end_date = get_last_day(nowdate())
salary_slip.save()
salary_slip.submit()
no_of_days = self.get_no_of_days()
days_in_month = no_of_days[0]
no_of_holidays = no_of_days[1]
self.assertEqual(salary_slip.payment_days, days_in_month - no_of_holidays)
# gross pay calculation based on attendance (payment days)
gross_pay = 78100 - ((78000 / (days_in_month - no_of_holidays)) * flt(salary_slip.leave_without_pay))
self.assertEqual(salary_slip.gross_pay, flt(gross_pay, 2))
def test_employee_salary_slip_read_permission(self):
make_employee("test_employee@salary.com")
@@ -175,7 +210,7 @@ class TestSalarySlip(unittest.TestCase):
# as per assigned salary structure 40500 in monthly salary so 236000*5/100/12
frappe.db.sql("""delete from `tabPayroll Period`""")
frappe.db.sql("""delete from `tabSalary Component`""")
payroll_period = create_payroll_period()
create_tax_slab(payroll_period, allow_tax_exemption=True)

View File

@@ -124,11 +124,12 @@ def get_allocated_and_expired_leaves(records, from_date, to_date):
def get_leave_ledger_entries(from_date, to_date, employee, leave_type):
records= frappe.db.sql("""
SELECT
employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type
is_carry_forward, is_expired
employee, leave_type, from_date, to_date, leaves, transaction_name,
transaction_type, is_carry_forward, is_expired
FROM `tabLeave Ledger Entry`
WHERE employee=%(employee)s AND leave_type=%(leave_type)s
AND docstatus=1
AND transaction_type = 'Leave Allocation'
AND (from_date between %(from_date)s AND %(to_date)s
OR to_date between %(from_date)s AND %(to_date)s
OR (from_date < %(from_date)s AND to_date > %(to_date)s))

View File

@@ -138,6 +138,8 @@ def update_employee(employee, details, date=None, cancel=False):
new_data = getdate(new_data)
elif fieldtype =="Datetime" and new_data:
new_data = get_datetime(new_data)
elif fieldtype in ["Currency", "Float"] and new_data:
new_data = flt(new_data)
setattr(employee, item.fieldname, new_data)
if item.fieldname in ["department", "designation", "branch"]:
internal_work_history[item.fieldname] = item.new

View File

@@ -27,6 +27,9 @@ frappe.ui.form.on('Maintenance Visit', {
if (frm.doc.__islocal) {
frm.set_value({mntc_date: frappe.datetime.get_today()});
}
if (frm.doc.purposes.length && frm.doc.purposes[0].item_name == undefined) {
frm.clear_table("purposes");
}
},
customer: function(frm) {
erpnext.utils.get_party_details(frm);

View File

@@ -240,6 +240,9 @@ class BOM(WebsiteGenerator):
existing_bom_cost = self.total_cost
for d in self.get("items"):
if not d.item_code:
continue
rate = self.get_rm_rate({
"company": self.company,
"item_code": d.item_code,
@@ -549,7 +552,7 @@ class BOM(WebsiteGenerator):
for d in self.get('items'):
if d.bom_no:
self.get_child_exploded_items(d.bom_no, d.stock_qty)
else:
elif d.item_code:
self.add_to_cur_exploded_items(frappe._dict({
'item_code' : d.item_code,
'item_name' : d.item_name,

View File

@@ -88,7 +88,7 @@ def get_bom_stock(filters):
GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1)
def get_manufacturer_records():
details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
details = frappe.get_all('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no", "parent"])
manufacture_details = frappe._dict()
for detail in details:
dic = manufacture_details.setdefault(detail.get('parent'), {})

View File

@@ -686,4 +686,4 @@ erpnext.patches.v12_0.purchase_receipt_status
erpnext.patches.v12_0.add_company_link_to_einvoice_settings
erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
erpnext.patches.v12_0.create_taxable_value_field_in_purchase_invoice
erpnext.patches.v12_0.show_einvoice_irn_cancelled_field
erpnext.patches.v12_0.show_einvoice_irn_cancelled_field

View File

@@ -6,6 +6,9 @@ import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
# updating column value to handle field change from Data to Currency
frappe.db.sql("update `tabBOM` set base_scrap_material_cost = '0' where trim(coalesce(base_scrap_material_cost, ''))= ''")
for doctype in ['BOM Explosion Item', 'BOM Item', 'Work Order Item', 'Item']:
if frappe.db.has_column(doctype, 'allow_transfer_for_manufacture'):
if doctype != 'Item':
@@ -26,4 +29,4 @@ def execute():
else:
frappe.db.sql(""" UPDATE `tab%s`
SET transfer_material_against = 'Work Order'
WHERE docstatus < 2""" % (doctype))
WHERE docstatus < 2""" % (doctype))

View File

@@ -86,6 +86,10 @@ class Project(Document):
if self.sales_order:
frappe.db.set_value("Sales Order", self.sales_order, "project", self.name)
def on_trash(self):
for so in frappe.get_all("Sales Order", {"project": self.name}, ["name"]):
frappe.db.set_value("Sales Order", so.get('name'), "project", "")
def update_percent_complete(self):
if self.percent_complete_method == "Manual":
if self.status == "Completed":

View File

@@ -8,7 +8,8 @@ test_records = frappe.get_test_records('Project')
test_ignore = ["Sales Order"]
from erpnext.projects.doctype.project_template.test_project_template import get_project_template, make_project_template
from erpnext.projects.doctype.project.project import set_project_status
from erpnext.selling.doctype.sales_order.sales_order import make_project as make_project_from_so
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from frappe.utils import getdate
@@ -32,6 +33,21 @@ class TestProject(unittest.TestCase):
self.assertEqual(task4.subject, 'Task 4')
self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06'))
def test_project_linking_with_sales_order(self):
so = make_sales_order()
project = make_project_from_so(so.name)
project.save()
self.assertEqual(project.sales_order, so.name)
so.reload()
self.assertEqual(so.project, project.name)
project.delete()
so.reload()
self.assertFalse(so.project)
def get_project(name):
template = get_project_template()

View File

@@ -20,10 +20,6 @@ class TestTimesheet(unittest.TestCase):
for dt in ["Salary Slip", "Salary Structure", "Salary Structure Assignment", "Timesheet"]:
frappe.db.sql("delete from `tab%s`" % dt)
if not frappe.db.exists("Salary Component", "Timesheet Component"):
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
def test_timesheet_billing_amount(self):
make_salary_structure_for_timesheet("_T-Employee-00001")
timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1)
@@ -177,6 +173,9 @@ def make_salary_structure_for_timesheet(employee):
salary_structure_name = "Timesheet Salary Structure Test"
frequency = "Monthly"
if not frappe.db.exists("Salary Component", "Timesheet Component"):
frappe.get_doc({"doctype": "Salary Component", "salary_component": "Timesheet Component"}).insert()
salary_structure = make_salary_structure(salary_structure_name, frequency, dont_submit=True)
salary_structure.salary_component = "Timesheet Component"
salary_structure.salary_slip_based_on_timesheet = 1

View File

@@ -940,7 +940,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
$.each(this.frm.doc.taxes || [], function(i, d) {
if(d.charge_type == "Actual") {
frappe.model.set_value(d.doctype, d.name, "tax_amount",
flt(d.tax_amount) / flt(exchange_rate));
flt(d.base_tax_amount) / flt(exchange_rate));
}
});
},

View File

@@ -9,6 +9,10 @@
"section_break_2",
"sandbox_mode",
"credentials",
"advanced_settings_section",
"client_id",
"column_break_8",
"client_secret",
"auth_token",
"token_expiry"
],
@@ -48,12 +52,32 @@
"fieldname": "sandbox_mode",
"fieldtype": "Check",
"label": "Sandbox Mode"
},
{
"collapsible": 1,
"fieldname": "advanced_settings_section",
"fieldtype": "Section Break",
"label": "Advanced Settings"
},
{
"fieldname": "client_id",
"fieldtype": "Data",
"label": "Client ID"
},
{
"fieldname": "client_secret",
"fieldtype": "Password",
"label": "Client Secret"
},
{
"fieldname": "column_break_8",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-01-13 12:04:49.449199",
"modified": "2021-11-16 19:50:28.029517",
"modified_by": "Administrator",
"module": "Regional",
"name": "E Invoice Settings",

View File

@@ -23,9 +23,5 @@
"StateCesAmt": "{item.state_cess_amount}",
"StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}",
"OthChrg": "{item.other_charges}",
"TotItemVal": "{item.total_value}",
"BchDtls": {{
"Nm": "{item.batch_no}",
"ExpDt": "{item.batch_expiry_date}"
}}
"TotItemVal": "{item.total_value}"
}}

View File

@@ -36,6 +36,7 @@ def validate_eligibility(doc):
return False
invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
invalid_company_gstin = not frappe.db.get_value('E Invoice User', {'gstin': doc.get('company_gstin')})
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
@@ -44,7 +45,7 @@ def validate_eligibility(doc):
no_taxes_applied = not doc.get('taxes') and not doc.get('gst_category') == 'Overseas'
has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
if invalid_company or invalid_company_gstin or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
return False
return True
@@ -200,8 +201,6 @@ def get_item_list(invoice):
item.taxable_value = abs(item.taxable_value)
item.discount_amount = 0
item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N'
item.serial_no = ""
@@ -308,7 +307,7 @@ def update_other_charges(tax_row, invoice_value_details, gst_accounts_list, invo
def get_payment_details(invoice):
payee_name = invoice.company
mode_of_payment = ', '.join([d.mode_of_payment for d in invoice.payments])
mode_of_payment = ""
paid_amount = invoice.base_paid_amount
outstanding_amount = invoice.outstanding_amount
@@ -459,10 +458,16 @@ def make_einvoice(invoice):
try:
einvoice = safe_json_load(einvoice)
einvoice = santize_einvoice_fields(einvoice)
except json.JSONDecodeError:
raise
except Exception:
show_link_to_error_log(invoice, einvoice)
validate_totals(einvoice)
try:
validate_totals(einvoice)
except Exception:
log_error(einvoice)
raise
return einvoice
@@ -534,7 +539,14 @@ def safe_json_load(json_string):
pos = e.pos
start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
snippet = json_string[start:end]
frappe.throw(_("Error in input data. Please check for any special characters near following input: <br> {}").format(snippet))
frappe.throw(
_(
"Error in input data. Please check for any special characters near following input: <br> {}"
).format(snippet),
title=_("Invalid JSON"),
exc=e,
)
def throw_error_list(errors, title):
if len(errors) > 1:
@@ -611,10 +623,17 @@ class GSPConnector():
request_log.save(ignore_permissions=True)
frappe.db.commit()
def get_client_credentials(self):
if self.e_invoice_settings.client_id and self.e_invoice_settings.client_secret:
return self.e_invoice_settings.client_id, self.e_invoice_settings.get_password('client_secret')
return frappe.conf.einvoice_client_id, frappe.conf.einvoice_client_secret
def fetch_auth_token(self):
client_id, client_secret = self.get_client_credentials()
headers = {
'gspappid': frappe.conf.einvoice_client_id,
'gspappsecret': frappe.conf.einvoice_client_secret
'gspappid': client_id,
'gspappsecret': client_secret
}
res = {}
try:
@@ -757,12 +776,13 @@ class GSPConnector():
headers = self.get_headers()
eway_bill_details = get_eway_bill_details(args)
data = json.dumps({
'Irn': args.irn,
'Distance': cint(eway_bill_details.distance),
'TransMode': eway_bill_details.mode_of_transport,
'TransId': eway_bill_details.gstin,
'TransName': eway_bill_details.transporter,
'TransName': eway_bill_details.name,
'TrnDocDt': eway_bill_details.document_date,
'TrnDocNo': eway_bill_details.document_name,
'VehNo': eway_bill_details.vehicle_no,
@@ -854,7 +874,7 @@ class GSPConnector():
if errors:
throw_error_list(errors, title)
else:
link_to_error_list = '<a href="desk#List/Error Log/List?method=E Invoice Request Failed">Error Log</a>'
link_to_error_list = '<a href="desk#List/Error Log/List?method=E Invoice Request Failed" target="_blank">Error Log</a>'
frappe.msgprint(
_('An error occurred while making e-invoicing request. Please check {} for more information.').format(link_to_error_list),
title=title,

View File

@@ -30,12 +30,13 @@ def validate_gstin_for_india(doc, method):
gst_category = []
if len(doc.links):
link_doctype = doc.links[0].get("link_doctype")
link_name = doc.links[0].get("link_name")
if hasattr(doc, 'gst_category'):
if len(doc.links):
link_doctype = doc.links[0].get("link_doctype")
link_name = doc.links[0].get("link_name")
if link_doctype in ["Customer", "Supplier"]:
gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
if link_doctype in ["Customer", "Supplier"]:
gst_category = frappe.db.get_value(link_doctype, {'name': link_name}, ['gst_category'])
doc.gstin = doc.gstin.upper().strip()
if not doc.gstin or doc.gstin == 'NA':
@@ -68,10 +69,9 @@ def validate_tax_category(doc, method):
def update_gst_category(doc, method):
for link in doc.links:
if link.link_doctype in ['Customer', 'Supplier']:
if doc.get('gstin'):
frappe.db.sql("""
UPDATE `tab{0}` SET gst_category = %s WHERE name = %s AND gst_category = 'Unregistered'
""".format(link.link_doctype), ("Registered Regular", link.link_name)) #nosec
meta = frappe.get_meta(link.link_doctype)
if doc.get('gstin') and meta.has_field('gst_category'):
frappe.db.set_value(link.link_doctype, {'name': link.link_name, 'gst_category': 'Unregistered'}, 'gst_category', 'Registered Regular')
def set_gst_state_and_state_number(doc):
if not doc.gst_state:

View File

@@ -104,14 +104,14 @@ def set_address_details(row, special_characters):
row.update({'ship_to_state': row.to_state})
def set_taxes(row, filters):
taxes = frappe.get_list("Sales Taxes and Charges",
taxes = frappe.get_all("Sales Taxes and Charges",
filters={
'parent': row.dn_id
},
fields=('item_wise_tax_detail', 'account_head'))
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
taxes_list = frappe.get_list("GST Account",
taxes_list = frappe.get_all("GST Account",
filters={
"parent": "GST Settings",
"company": filters.company

View File

@@ -175,7 +175,9 @@ def add_new_address(doc):
def create_lead_for_item_inquiry(lead, subject, message):
lead = frappe.parse_json(lead)
lead_doc = frappe.new_doc('Lead')
lead_doc.update(lead)
for fieldname in ("lead_name", "company_name", "email_id", "phone"):
lead_doc.set(fieldname, lead.get(fieldname))
lead_doc.set('lead_owner', '')
try:

View File

@@ -206,10 +206,11 @@ class Item(WebsiteGenerator):
'route')) + '/' + self.scrub((self.item_name if self.item_name else self.item_code) + '-' + random_string(5))
def validate_website_image(self):
"""Validate if the website image is a public file"""
if frappe.flags.in_import:
return
"""Validate if the website image is a public file"""
auto_set_website_image = False
if not self.website_image and self.image:
auto_set_website_image = True
@@ -239,10 +240,11 @@ class Item(WebsiteGenerator):
self.website_image = None
def make_thumbnail(self):
"""Make a thumbnail of `website_image`"""
if frappe.flags.in_import:
return
"""Make a thumbnail of `website_image`"""
import requests.exceptions
if not self.is_new() and self.website_image != frappe.db.get_value(self.doctype, self.name, "website_image"):
@@ -688,7 +690,6 @@ class Item(WebsiteGenerator):
def recalculate_bin_qty(self, new_name):
from erpnext.stock.stock_balance import repost_stock
frappe.db.auto_commit_on_many_writes = 1
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
@@ -702,7 +703,6 @@ class Item(WebsiteGenerator):
repost_stock(new_name, warehouse)
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
frappe.db.auto_commit_on_many_writes = 0
def copy_specification_from_item_group(self):
self.set("website_specifications", [])

View File

@@ -468,7 +468,7 @@ class TestItem(unittest.TestCase):
item_doc.save()
# Check values saved correctly
barcodes = frappe.get_list(
barcodes = frappe.get_all(
'Item Barcode',
fields=['barcode', 'barcode_type'],
filters={'parent': item_code})

View File

@@ -36,7 +36,8 @@
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Qty"
"label": "Qty",
"reqd": 1
},
{
"fieldname": "picked_qty",
@@ -180,7 +181,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-03-13 19:08:21.995986",
"modified": "2021-09-28 12:02:16.923056",
"modified_by": "Administrator",
"module": "Stock",
"name": "Pick List Item",
@@ -190,4 +191,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -78,7 +78,11 @@ frappe.ui.form.on('Stock Entry', {
}
}
filters["warehouse"] = item.s_warehouse || item.t_warehouse;
// User could want to select a manually created empty batch (no warehouse)
// or a pre-existing batch
if (frm.doc.purpose != "Material Receipt") {
filters["warehouse"] = item.s_warehouse || item.t_warehouse;
}
return {
query : "erpnext.controllers.queries.get_batch_no",
@@ -282,6 +286,12 @@ frappe.ui.form.on('Stock Entry', {
frm.trigger("setup_quality_inspection");
},
before_save: function(frm) {
frm.doc.items.forEach((item) => {
item.uom = item.uom || item.stock_uom;
})
},
purpose: function(frm) {
frm.trigger('validate_purpose_consumption');
frm.fields_dict.items.grid.refresh();

View File

@@ -537,6 +537,11 @@ def get_stock_balance_for(item_code, warehouse,
item_dict = frappe.db.get_value("Item", item_code,
["has_serial_no", "has_batch_no"], as_dict=1)
if not item_dict:
# In cases of data upload to Items table
msg = _("Item {} does not exist.").format(item_code)
frappe.throw(msg, title=_("Missing"))
serial_nos = ""
with_serial_no = True if item_dict.get("has_serial_no") else False
data = get_stock_balance(item_code, warehouse, posting_date, posting_time,

View File

@@ -32,64 +32,15 @@ class TestWarehouse(unittest.TestCase):
self.assertEqual(p_warehouse.name, child_warehouse.parent_warehouse)
self.assertEqual(child_warehouse.is_group, 0)
def test_warehouse_renaming(self):
set_perpetual_inventory(1)
create_warehouse("Test Warehouse for Renaming 1")
account = get_inventory_account("_Test Company", "Test Warehouse for Renaming 1 - _TC")
self.assertTrue(frappe.db.get_value("Warehouse", filters={"account": account}))
def test_naming(self):
company = "Wind Power LLC"
warehouse_name = "Named Warehouse - WP"
wh = frappe.get_doc(doctype="Warehouse", warehouse_name=warehouse_name, company=company).insert()
self.assertEqual(wh.name, warehouse_name)
# Rename with abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
# Rename without abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Renaming 1 - _TC"}))
# Another rename with multiple dashes
if frappe.db.exists("Warehouse", "Test - Warehouse - Company - _TC"):
frappe.delete_doc("Warehouse", "Test - Warehouse - Company - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC", "Test - Warehouse - Company")
def test_warehouse_merging(self):
set_perpetual_inventory(1)
create_warehouse("Test Warehouse for Merging 1")
create_warehouse("Test Warehouse for Merging 2")
make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 1 - _TC",
qty=1, rate=100)
make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC",
qty=1, rate=100)
existing_bin_qty = (
cint(frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty"))
+ cint(frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty"))
)
rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC",
"Test Warehouse for Merging 2 - _TC", merge=True)
self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC"))
bin_qty = frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")
self.assertEqual(bin_qty, existing_bin_qty)
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Merging 2 - _TC"}))
warehouse_name = "Unnamed Warehouse"
wh = frappe.get_doc(doctype="Warehouse", warehouse_name=warehouse_name, company=company).insert()
self.assertIn(warehouse_name, wh.name)
def create_warehouse(warehouse_name, properties=None, company=None):
if not company:
@@ -145,4 +96,4 @@ def get_group_stock_account(company, company_abbr=None):
if not company_abbr:
company_abbr = frappe.get_cached_value("Company", company, 'abbr')
group_stock_account = "Current Assets - " + company_abbr
return group_stock_account
return group_stock_account

View File

@@ -1,6 +1,5 @@
{
"allow_import": 1,
"allow_rename": 1,
"creation": "2013-03-07 18:50:32",
"description": "A logical Warehouse against which stock entries are made.",
"doctype": "DocType",
@@ -235,7 +234,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2020-08-03 18:41:52.442502",
"modified": "2021-12-03 04:40:06.414630",
"modified_by": "Administrator",
"module": "Stock",
"name": "Warehouse",

View File

@@ -7,6 +7,7 @@ from frappe.utils import cint, flt
from frappe import throw, _
from collections import defaultdict
from frappe.utils.nestedset import NestedSet
from erpnext.stock import get_warehouse_account
from frappe.contacts.address_and_contact import load_address_and_contact
@@ -18,8 +19,9 @@ class Warehouse(NestedSet):
suffix = " - " + frappe.get_cached_value('Company', self.company, "abbr")
if not self.warehouse_name.endswith(suffix):
self.name = self.warehouse_name + suffix
else:
self.name = self.warehouse_name
return
self.name = self.warehouse_name
def onload(self):
'''load account name for General Ledger Report'''
@@ -63,57 +65,6 @@ class Warehouse(NestedSet):
return frappe.db.sql("""select name from `tabWarehouse`
where parent_warehouse = %s limit 1""", self.name)
def before_rename(self, old_name, new_name, merge=False):
super(Warehouse, self).before_rename(old_name, new_name, merge)
# Add company abbr if not provided
new_warehouse = erpnext.encode_company_abbr(new_name, self.company)
if merge:
if not frappe.db.exists("Warehouse", new_warehouse):
frappe.throw(_("Warehouse {0} does not exist").format(new_warehouse))
if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"):
frappe.throw(_("Both Warehouse must belong to same Company"))
return new_warehouse
def after_rename(self, old_name, new_name, merge=False):
super(Warehouse, self).after_rename(old_name, new_name, merge)
new_warehouse_name = self.get_new_warehouse_name_without_abbr(new_name)
self.db_set("warehouse_name", new_warehouse_name)
if merge:
self.recalculate_bin_qty(new_name)
def get_new_warehouse_name_without_abbr(self, name):
company_abbr = frappe.get_cached_value('Company', self.company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() == company_abbr.lower():
name = parts[0]
return name
def recalculate_bin_qty(self, new_name):
from erpnext.stock.stock_balance import repost_stock
frappe.db.auto_commit_on_many_writes = 1
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
repost_stock_for_items = frappe.db.sql_list("""select distinct item_code
from tabBin where warehouse=%s""", new_name)
# Delete all existing bins to avoid duplicate bins for the same item and warehouse
frappe.db.sql("delete from `tabBin` where warehouse=%s", new_name)
for item_code in repost_stock_for_items:
repost_stock(item_code, new_name)
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
frappe.db.auto_commit_on_many_writes = 0
def convert_to_group_or_ledger(self):
if self.is_group:
self.convert_to_ledger()
@@ -232,4 +183,4 @@ def get_warehouses_based_on_account(account, company=None):
frappe.throw(_("Warehouse not found against the account {0}")
.format(account))
return warehouses
return warehouses

View File

@@ -329,7 +329,7 @@ def get_basic_details(args, item, overwrite_warehouse=True):
if not out[d[1]]:
out[d[1]] = frappe.get_cached_value('Company', args.company, d[2]) if d[2] else None
for fieldname in ("item_name", "item_group", "barcodes", "brand", "stock_uom"):
for fieldname in ("item_name", "item_group", "brand", "stock_uom"):
out[fieldname] = item.get(fieldname)
if args.get("manufacturer"):

View File

@@ -39,8 +39,8 @@ def execute(filters=None):
item_balance.setdefault((item, item_map[item]["item_group"]), [])
total_stock_value = 0.00
for wh in warehouse_list:
row += [qty_dict.bal_qty] if wh.name in warehouse else [0.00]
total_stock_value += qty_dict.bal_val if wh.name in warehouse else 0.00
row += [qty_dict.bal_qty] if wh.name == warehouse else [0.00]
total_stock_value += qty_dict.bal_val if wh.name == warehouse else 0.00
item_balance[(item, item_map[item]["item_group"])].append(row)
item_value.setdefault((item, item_map[item]["item_group"]),[])

View File

@@ -368,7 +368,7 @@ class update_entries_after(object):
batch = self.stock_queue[index]
if qty_to_pop >= batch[0]:
# consume current batch
qty_to_pop = qty_to_pop - batch[0]
qty_to_pop = _round_off_if_near_zero(qty_to_pop - batch[0])
self.stock_queue.pop(index)
if not self.stock_queue and qty_to_pop:
# stock finished, qty still remains to be withdrawn
@@ -382,8 +382,8 @@ class update_entries_after(object):
batch[0] = batch[0] - qty_to_pop
qty_to_pop = 0
stock_value = sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue))
stock_qty = sum((flt(batch[0]) for batch in self.stock_queue))
stock_value = _round_off_if_near_zero(sum((flt(batch[0]) * flt(batch[1]) for batch in self.stock_queue)))
stock_qty = _round_off_if_near_zero(sum((flt(batch[0]) for batch in self.stock_queue)))
if stock_qty:
self.valuation_rate = stock_value / flt(stock_qty)
@@ -549,3 +549,12 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no,
frappe.throw(msg=msg, title=_("Valuation Rate Missing"))
return valuation_rate
def _round_off_if_near_zero(number, precision = 7):
"""Rounds off the number to zero only if number is close to zero for decimal
specified in precision. Precision defaults to 7.
"""
if abs(0.0 - flt(number)) < (1.0 / (10**precision)):
return 0.0
return flt(number)

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,9 @@ braintree==3.57.1
# frappe # https://github.com/frappe/frappe is installed during bench-init
gocardless-pro==1.11.0
googlemaps==3.1.1
pandas==0.24.2
pandas>=0.24.0,<1.2.0
plaid-python~=7.2.1
PyGithub==1.44.1
python-stdnum==1.12
Unidecode==1.1.1
WooCommerce==2.1.1
pycryptodome==3.9.8