Compare commits

...

87 Commits

Author SHA1 Message Date
Saurabh
529cc1ca51 bumped to version 10.1.77 2019-01-15 19:46:36 +06:00
Nabin Hait
d36bc5a9fd Merge pull request #16327 from nabinhait/group_warehouse_stock_reco
Fetch items based on group warehouse in Stock Reconciliation
2019-01-15 17:49:07 +05:30
Nabin Hait
acd7f73d57 Merge branch 'hotfix' into group_warehouse_stock_reco 2019-01-14 20:25:53 +05:30
Nabin Hait
910a744263 Merge pull request #16391 from nabinhait/cc_fix
fix: Travis fix for hotfix branch
2019-01-14 20:24:23 +05:30
Nabin Hait
4ed7cfc515 tests(cost-center-company): Validate cost center's company and revent tests 2019-01-14 17:14:39 +05:30
Nabin Hait
0d208851a4 Fix(stock-reco): Fixed codacy issues 2019-01-10 17:56:11 +05:30
Sagar Vora
f99e013ebc fix: gstin validation should work when there is no state (#16378) 2019-01-10 11:57:24 +05:30
Nabin Hait
6b466fe230 Merge pull request #16326 from hrwX/hotfix-gst-fix
fix: GSTIN validation
2019-01-10 11:23:42 +05:30
Sagar Vora
07cf4e8f5b fix: use division consistent with Python 3 & other changes 2019-01-10 11:14:58 +05:30
karthikeyan5
2825b929c1 fix(GSTIN Validation - india): added checksum validation for GSTIN 2019-01-09 19:15:10 +05:30
Sagar Vora
d40743a570 fix: 14th digit may not be zero 2019-01-07 13:38:43 +05:30
Himanshu
75ab042632 removed lowercase regex 2019-01-04 17:13:43 +05:30
Himanshu
547229fef1 PAN card validation in GST 2019-01-03 23:20:38 +05:30
Nabin Hait
47c9826b6f test(reco-warehouse): Get items for group warehouse 2019-01-03 15:24:59 +05:30
Himanshu
53c040f838 Gst number validation fix 2019-01-03 12:50:18 +05:30
Himanshu
a09a6e4020 Merge pull request #2 from frappe/hotfix
Hotfix
2019-01-03 12:47:03 +05:30
Nabin Hait
cacbdec565 Merge pull request #16283 from rohitwaghchaure/fix_not_able_to_delete_customer
[Fix] Not able to delete customer if contact is available
2018-12-31 11:55:24 +05:30
Nabin Hait
fcbe410c2f feat(stock-reco): Fetch items based on group warehouse 2018-12-28 16:31:05 +05:30
Frappe Bot
23ee3c6fbe Merge branch 'hotfix' 2018-12-26 11:09:34 +00:00
Frappe Bot
9a9c9c2ba3 bumped to version 10.1.76 2018-12-26 11:09:34 +00:00
Rohit Waghchaure
52a692ee08 [Fix] Not able to delete customer if contact is available 2018-12-26 02:34:13 +05:30
rohitwaghchaure
7ec5e80b70 [Fix] While making sales invoice from delivery note, system not remove the returned qty (#16141) 2018-12-24 14:55:31 +05:30
Navdeep Ghai
c4d38c0afc Fix the disappears of image bug after uploading and saving the employee (#16217)
* Fix image disappears of image after uploading and saving the employee

* Fix the codacy issue
2018-12-24 14:53:19 +05:30
Shreya Shah
91ddadeefa fix: add currency to options (#16199) 2018-12-24 14:25:12 +05:30
Shreya Shah
d2b9093ecc fix: Check if items exist (#16248) 2018-12-24 14:21:07 +05:30
Raffael Meyer
ed3561279d Update README.md (#16224) 2018-12-24 14:19:51 +05:30
Nabin Hait
e9a2d57357 Merge pull request #16249 from shreyashah115/error-fix-2
Purchase order validation sql error
2018-12-21 10:59:20 +05:30
shreyashah115@gmail.com
ba8c041206 fix: Check if items exist 2018-12-20 13:23:51 +05:30
Frappe Bot
402ffa8b29 Merge branch 'hotfix' 2018-12-10 12:14:33 +00:00
Frappe Bot
65a9991d84 bumped to version 10.1.75 2018-12-10 12:14:33 +00:00
Nabin Hait
3248e0bb3e Merge pull request #16163 from netchampfaris/hotfix-setupwizard-abbr
fix(setup-wizard): Validate abbr length before switching to next slide
2018-12-07 15:20:23 +05:30
Nabin Hait
1b529d997c Merge pull request #16146 from rohitwaghchaure/remove_tax_amt_from_the_billed_amt_in_project
[Fix] In project, tax amount is added in the total billed amount(sales) field
2018-12-07 15:17:35 +05:30
Nabin Hait
01ccb8b099 Merge pull request #16144 from deepeshgarg007/list_view
[Fix]List view currency bug fix
2018-12-07 15:09:09 +05:30
Deepesh Garg
6967b44ac8 Merge branch 'hotfix' into list_view 2018-12-07 08:30:20 +05:30
Frappe Bot
efb2712d14 Merge branch 'hotfix' 2018-12-06 09:14:35 +00:00
Frappe Bot
be0a349a52 bumped to version 10.1.74 2018-12-06 09:14:35 +00:00
Nabin Hait
32c95c3292 Merge pull request #16113 from nabinhait/travis-fix
fix(test): Fixed tests for discount with margin and period closing voucher
2018-12-06 11:58:56 +05:30
Faris Ansari
40c15348da fix(setup-wizard): Validate abbr length before switching to next slide 2018-12-06 07:37:32 +05:30
rohitwaghchaure
f85856d9f8 Merge pull request #16155 from rohitwaghchaure/changed_parent_for_item_attribute_value
[Fix] Permissions issue
2018-12-04 18:45:07 +05:30
Rohit Waghchaure
675567419b [Fix] Permissions issue 2018-12-04 18:43:52 +05:30
Rohit Waghchaure
636d186d08 [Fix] In project, tax amount is added in the total billed amount(sales) field 2018-12-03 16:04:08 +05:30
deepeshgarg007
114cc810a0 List view currency bug fix 2018-12-03 15:13:06 +05:30
Nabin Hait
6b93f2fe0a Merge pull request #16139 from Zlash65/fix-assess
[Minor] Assessment Result fix
2018-12-03 14:33:01 +05:30
Nabin Hait
0b4ea25f83 Merge pull request #16138 from rohitwaghchaure/user_permissions_issue_item
[Fix] Permissions issue while making item variants
2018-12-03 14:32:40 +05:30
Nabin Hait
dd7b020b01 fix(test): Fixed tests for period closing voucher 2018-12-03 14:24:41 +05:30
Nabin Hait
dce68422a7 Merge pull request #16133 from rohitwaghchaure/fixed_stock_value_diifference_calculation_for_stock_reco
[Fix] Stock value difference calculation for stock reconciliation
2018-12-03 14:20:40 +05:30
Zlash65
db43c3af68 display message if details to fetch grade isnt filled up 2018-12-03 12:15:10 +05:30
Rohit Waghchaure
a8003c1af7 [Fix] Permissions issue while making item variants 2018-12-03 11:49:52 +05:30
rohitwaghchaure
ce72d26e95 Merge pull request #16125 from frappe/revert-16112-accounts_receivable_summary_print_issue
Revert "[Fix] Accounts receivable summary print is not working"
2018-11-30 16:21:53 +05:30
Rohit Waghchaure
16b8ecbe05 [Fix] Stock value difference calculation for stock reconciliation 2018-11-30 16:20:52 +05:30
Nabin Hait
eda0a6eb33 fix(test): Fixed tests for period closing voucher 2018-11-30 15:15:17 +05:30
rohitwaghchaure
ca332bdcdb Revert "[Fix] Accounts receivable summary print is not working" 2018-11-29 18:15:29 +05:30
Ameya Shenoy
e3136c38e1 bumped to version 10.1.73 2018-11-28 07:57:27 +00:00
Nabin Hait
ce870316c5 Merge pull request #16112 from rohitwaghchaure/accounts_receivable_summary_print_issue
[Fix] Accounts receivable summary print is not working
2018-11-27 17:57:17 +05:30
Nabin Hait
f642f86982 Merge branch 'hotfix' into accounts_receivable_summary_print_issue 2018-11-27 17:56:49 +05:30
Nabin Hait
96598936ad fix(test): Fixed tests for discount with margin and period closing voucher 2018-11-27 17:48:13 +05:30
Rohit Waghchaure
10bcd5113c [Fix] Accounts receivable summary print is not working 2018-11-27 16:25:35 +05:30
Nabin Hait
b98d08a2cb Merge pull request #16049 from rohitwaghchaure/incorrect_stock_value_difference_calculation
[Fix] Incorrect stock value difference because of negative stock
2018-11-27 13:21:43 +05:30
Nabin Hait
ccb03fb255 Merge pull request #16073 from deepeshgarg007/receivable-summary
Accounts receivable summary print fix
2018-11-27 13:11:37 +05:30
Nabin Hait
3c31e4ad4b Merge pull request #16088 from rohitwaghchaure/fixed_pos_profile_packing_items
If is pos is enabled and no pos profile then use the item's default warehouse for packing materials
2018-11-27 13:10:14 +05:30
Nabin Hait
0ccc05bdf7 Merge pull request #15997 from rohitwaghchaure/address_not_set_from_api
[Fix] Address not set from the API
2018-11-26 20:17:03 +05:30
Nabin Hait
7f02659425 Merge branch 'hotfix' into address_not_set_from_api 2018-11-26 20:16:54 +05:30
Nabin Hait
2598c475ed Merge pull request #15999 from codingCoffee/css
css: build files in v10
2018-11-26 20:02:18 +05:30
Rohit Waghchaure
3893ba302c If is pos and no pos profile use the item's default warehouse for packing materials 2018-11-26 14:13:24 +05:30
deepeshgarg007
5dbb9f5312 Accounts receivable summary print fix 2018-11-23 19:54:08 +05:30
Rohit Waghchaure
6424f47f3b [Fix] Incorrect stock value difference because of negative stock 2018-11-21 23:18:41 +05:30
Ameya Shenoy
27a0c37963 css: build files in v10 2018-11-15 19:21:30 +00:00
Rohit Waghchaure
f4dc71661d [Fix] Address not set from the API 2018-11-15 17:04:02 +05:30
Frappe Bot
0b697c5298 Merge branch 'hotfix' 2018-11-15 09:04:14 +00:00
Frappe Bot
2426d00dd6 bumped to version 10.1.72 2018-11-15 09:04:14 +00:00
rohitwaghchaure
5395b164cd Merge pull request #15983 from rohitwaghchaure/fixed_claimed_amount_issue
Claimed amount is not reset on cancel of employee advance
2018-11-14 13:21:11 +05:30
Rohit Waghchaure
047a7123eb Claimed amount is not reset on cancel 2018-11-14 13:20:00 +05:30
Frappe Bot
cb1726d6ec Merge branch 'hotfix' 2018-11-13 10:55:31 +00:00
Frappe Bot
6b78e520d2 bumped to version 10.1.71 2018-11-13 10:55:31 +00:00
khushalti
fad08e1bcb [fix] - GL entry not creating on PR if rejected qty mentoned (#15815) 2018-11-13 12:09:32 +05:30
Ameya Shenoy
3d58576797 Merge pull request #15963 from codingCoffee/frankfurter
fix: changed currency exchange API to frankfurter
2018-11-12 17:55:48 +05:30
Ameya Shenoy
438490fd29 fix: changed currency exchange API to frankfurter
- Switched to Frankfurter's public API (frankfurter.app) from
self hosted API (frankfurter.erpnext.org)
2018-11-12 15:15:06 +05:30
Frappe Bot
f001a9eb4f Merge branch 'hotfix' 2018-11-09 10:09:04 +00:00
Frappe Bot
f8a5cfe8d8 bumped to version 10.1.70 2018-11-09 10:09:04 +00:00
rohitwaghchaure
a88eaa6e20 [Fix] User able to change discount if pricing rule has discount value as zero (#15921) 2018-11-08 13:05:08 +05:30
Nabin Hait
6ff2f395f5 fix(gle): Post gl entry for booking COGS from Sales Invoice even if grand_total is zero (#15825) 2018-11-08 12:30:44 +05:30
Frappe Bot
ccff3e2aec Merge branch 'hotfix' 2018-11-07 11:26:10 +00:00
Frappe Bot
7f37d26f05 bumped to version 10.1.69 2018-11-07 11:26:10 +00:00
Shreya Shah
c704630d5f fix: Return default if company not found (#15915) 2018-11-06 17:24:45 +05:30
Ameya Shenoy
1a41929c6b Merge branch 'hotfix' 2018-11-06 11:02:04 +00:00
Ameya Shenoy
a97a520aa8 bumped to version 10.1.68 2018-11-06 11:02:04 +00:00
Nabin Hait
3e6663d47e fix(serial no): Set supplier info based on pur invoice if serial no is created from Purchase Invoice (#15820) 2018-11-05 14:01:59 +05:30
45 changed files with 474 additions and 121 deletions

View File

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

View File

@@ -18,15 +18,14 @@ class GLEntry(Document):
self.flags.ignore_submit_comment = True
self.check_mandatory()
self.validate_and_set_fiscal_year()
self.pl_must_have_cost_center()
self.validate_cost_center()
if not self.flags.from_repost:
self.pl_must_have_cost_center()
self.check_pl_account()
self.validate_cost_center()
self.validate_party()
self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False):
if not from_repost:
self.validate_account_details(adv_adj)

View File

@@ -70,12 +70,14 @@ class PeriodClosingVoucher(AccountsController):
net_pl_balance += flt(acc.balance_in_company_currency)
if net_pl_balance:
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
gl_entries.append(self.get_gl_dict({
"account": self.closing_account_head,
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
"cost_center": cost_center
}))
from erpnext.accounts.general_ledger import make_gl_entries

View File

@@ -72,6 +72,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
"company": "_Test Company",
"fiscal_year": get_fiscal_year(today(), company="_Test Company")[0],
"posting_date": today(),
"cost_center": "_Test Cost Center - _TC",
"remarks": "test"
})
pcv.insert()

View File

@@ -185,7 +185,8 @@ def get_pricing_rule_for_item(args):
"discount_percentage": 0.0
})
else:
item_details.discount_percentage = pricing_rule.discount_percentage or args.discount_percentage
item_details.discount_percentage = (pricing_rule.get('discount_percentage', 0)
if pricing_rule else args.discount_percentage)
elif args.get('pricing_rule'):
item_details = remove_pricing_rule_for_item(args.get("pricing_rule"), item_details)

View File

@@ -11,12 +11,16 @@ from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
class TestPricingRule(unittest.TestCase):
def setUp(self):
frappe.db.sql("delete from `tabPricing Rule`")
def tearDown(self):
frappe.db.sql("delete from `tabPricing Rule`")
def test_pricing_rule_for_discount(self):
from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
frappe.db.sql("delete from `tabPricing Rule`")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
@@ -88,14 +92,10 @@ class TestPricingRule(unittest.TestCase):
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 15)
frappe.db.sql("delete from `tabPricing Rule`")
def test_pricing_rule_for_margin(self):
from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
frappe.db.sql("delete from `tabPricing Rule`")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
@@ -109,14 +109,14 @@ class TestPricingRule(unittest.TestCase):
"company": "_Test Company"
}
frappe.get_doc(test_record.copy()).insert()
item_price = frappe.get_doc({
"doctype": "Item Price",
"price_list": "_Test Price List 2",
"item_code": "_Test FG Item 2",
"price_list_rate": 100
})
item_price.insert(ignore_permissions=True)
args = frappe._dict({
@@ -136,14 +136,10 @@ class TestPricingRule(unittest.TestCase):
self.assertEquals(details.get("margin_type"), "Percentage")
self.assertEquals(details.get("margin_rate_or_amount"), 10)
frappe.db.sql("delete from `tabPricing Rule`")
def test_pricing_rule_for_variants(self):
from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
frappe.db.sql("delete from `tabPricing Rule`")
if not frappe.db.exists("Item", "Test Variant PRT"):
frappe.get_doc({
"doctype": "Item",
@@ -209,8 +205,6 @@ class TestPricingRule(unittest.TestCase):
self.assertEquals(details.get("discount_percentage"), 17.5)
def test_pricing_rule_for_stock_qty(self):
frappe.db.sql("delete from `tabPricing Rule`")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
@@ -253,24 +247,18 @@ class TestPricingRule(unittest.TestCase):
def test_pricing_rule_with_margin_and_discount(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Pricing Rule')
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10)
make_pricing_rule(selling=1, margin_type="Percentage", margin_rate_or_amount=10, discount_percentage=10)
si = create_sales_invoice(do_not_save=True)
si.items[0].price_list_rate = 1000
si.payment_schedule = []
si.insert(ignore_permissions=True)
item = si.items[0]
self.assertEquals(item.rate, 1100)
self.assertEquals(item.margin_rate_or_amount, 10)
# With discount
item.discount_percentage = 10
si.payment_schedule = []
si.save()
item = si.items[0]
self.assertEquals(item.rate_with_margin, 1100)
self.assertEqual(item.discount_percentage, 10)
self.assertEquals(item.discount_amount, 110)
self.assertEquals(item.rate, 990)
self.assertEquals(item.discount_percentage, 10)
frappe.db.sql("delete from `tabPricing Rule`")
def make_pricing_rule(**args):
args = frappe._dict(args)

View File

@@ -589,9 +589,6 @@ class SalesInvoice(SellingController):
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
if not self.grand_total:
return
if not gl_entries:
gl_entries = self.get_gl_entries()

View File

@@ -194,7 +194,7 @@
{% if(!filters.show_pdc_in_print) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["Debit Note"], data[i]["currency"]) %} </td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
@@ -220,7 +220,7 @@
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{% } %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
@@ -230,7 +230,7 @@
{% } %}
<td style="text-align: right">{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("Credit Note Amt")], data[i]["currency"]) : format_currency(data[i][__("Debit Note Amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
{% } %}
{% } %}

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe import _, scrub
from frappe.utils import flt
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
@@ -18,25 +18,93 @@ class AccountsReceivableSummary(ReceivablePayableReport):
if party_naming_by == "Naming Series":
columns += [ args.get("party_type") + " Name::140"]
credit_debit_label = _("Credit Note Amt") if args.get('party_type') == 'Customer' else _("Debit Note Amt")
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
columns += [{
"label": _("Total Invoiced Amt"),
"fieldname": "total_invoiced_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
},
{
"label": _("Total Paid Amt"),
"fieldname": "total_paid_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
}]
columns += [
_("Total Invoiced Amt") + ":Currency/currency:140",
_("Total Paid Amt") + ":Currency/currency:140",
credit_debit_label + ":Currency/currency:140",
_("Total Outstanding Amt") + ":Currency/currency:160",
"0-" + str(self.filters.range1) + ":Currency/currency:100",
str(self.filters.range1) + "-" + str(self.filters.range2) + ":Currency/currency:100",
str(self.filters.range2) + "-" + str(self.filters.range3) + ":Currency/currency:100",
str(self.filters.range3) + _("-Above") + ":Currency/currency:100"]
{
"label": _(credit_debit_label),
"fieldname": scrub(credit_debit_label),
"fieldtype": "Currency",
"options": "currency",
"width": 140
},
{
"label": _("Total Outstanding Amt"),
"fieldname": "total_outstanding_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _("0-" + str(self.filters.range1)),
"fieldname": scrub("0-" + str(self.filters.range1)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range3) + _("-Above")),
"fieldname": scrub(str(self.filters.range3) + _("-Above")),
"fieldtype": "Currency",
"options": "currency",
"width": 160
}
]
if args.get("party_type") == "Customer":
columns += [
_("Territory") + ":Link/Territory:80",
_("Customer Group") + ":Link/Customer Group:120"
]
columns += [{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80
},
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"width": 80
}]
if args.get("party_type") == "Supplier":
columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
columns += [{
"label": _("Supplier Group"),
"fieldname": "supplier_group",
"fieldtype": "Link",
"options": "Supplier Group",
"width": 80
}]
columns.append({
"fieldname": "currency",
"label": _("Currency"),
@@ -67,7 +135,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row += [self.get_territory(party), self.get_customer_group(party)]
if args.get("party_type") == "Supplier":
row += [self.get_supplier_type(party)]
row.append(party_dict.currency)
data.append(row)
@@ -91,7 +159,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
for k in party_total[d.party].keys():
if k != "currency":
party_total[d.party][k] += flt(d.get(k, 0))
party_total[d.party].currency = d.currency
return party_total

View File

@@ -16,7 +16,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if not filters: filters = {}
columns = get_columns(additional_table_columns)
company_currency = erpnext.get_company_currency(filters.company)
company_currency = erpnext.get_company_currency(filters.get('company'))
item_list = get_items(filters, additional_query_columns)
if item_list:

View File

@@ -85,6 +85,7 @@ class PurchaseOrder(BuyingController):
frappe.msgprint(_("{0} currently has a {1} Supplier Scorecard standing, and Purchase Orders to this supplier should be issued with caution.").format(self.supplier, standing), title=_("Caution"), indicator='orange')
def validate_minimum_order_qty(self):
if not self.get("items"): return
items = list(set([d.item_code for d in self.get("items")]))
itemwise_min_order_qty = frappe._dict(frappe.db.sql("""select name, min_order_qty

View File

@@ -10,6 +10,7 @@ from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
from erpnext.stock.stock_ledger import get_valuation_rate
from frappe.contacts.doctype.address.address import get_address_display
from erpnext.controllers.stock_controller import StockController
@@ -39,6 +40,7 @@ class BuyingController(StockController):
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.validate_warehouse()
self.set_supplier_address()
if self.doctype=="Purchase Invoice":
self.validate_purchase_receipt_if_update_stock()
@@ -96,6 +98,16 @@ class BuyingController(StockController):
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
d.db_set('cost_center', lc_voucher_data[0][1])
def set_supplier_address(self):
address_dict = {
'supplier_address': 'address_display',
'shipping_address': 'shipping_address_display'
}
for address_field, address_display_field in address_dict.items():
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
def set_total_in_words(self):
from frappe.utils import money_in_words
if self.meta.get_field("base_in_words"):
@@ -450,7 +462,7 @@ class BuyingController(StockController):
update_last_purchase_rate(self, is_submit = 0)
def validate_schedule_date(self):
if not self.schedule_date:
if not self.schedule_date and self.get("items"):
self.schedule_date = min([d.schedule_date for d in self.get("items")])
if self.schedule_date:

View File

@@ -8,6 +8,7 @@ from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate
from erpnext.stock.get_item_details import get_conversion_factor
from frappe.contacts.doctype.address.address import get_address_display
from erpnext.controllers.stock_controller import StockController
@@ -40,6 +41,7 @@ class SellingController(StockController):
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
self.set_po_nos()
self.set_customer_address()
check_active_sales_items(self)
def set_missing_values(self, for_validate=False):
@@ -344,6 +346,17 @@ class SellingController(StockController):
po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
def set_customer_address(self):
address_dict = {
'customer_address': 'address_display',
'shipping_address_name': 'shipping_address',
'company_address': 'company_address_display'
}
for address_field, address_display_field in address_dict.items():
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
def validate_items(self):
# validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type

View File

@@ -66,8 +66,11 @@ class calculate_taxes_and_totals(object):
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))\
if item.rate_with_margin > 0 else item.rate
if flt(item.rate_with_margin) > 0:
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
item.discount_amount = item.rate_with_margin - item.rate
elif flt(item.price_list_rate) > 0:
item.discount_amount = item.price_list_rate - item.rate
item.net_rate = item.rate
item.amount = flt(item.rate * item.qty, item.precision("amount"))

View File

@@ -50,6 +50,12 @@ frappe.ui.form.on("Assessment Result", {
frappe.ui.form.on("Assessment Result Detail", {
score: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if(!d.maximum_score || !frm.doc.grading_scale) {
d.score = "";
frappe.throw(__("Please fill in all the details to generate Assessment Result."));
}
if (d.score > d.maximum_score) {
frappe.throw(__("Score cannot be greater than Maximum Score"));
}

View File

@@ -55,8 +55,8 @@ class Employee(NestedSet):
def validate_user_details(self):
data = frappe.db.get_value('User',
self.user_id, ['enabled', 'user_image'], as_dict=1)
self.image = data.get("user_image")
if data.get("user_image"):
self.image = data.get("user_image")
self.validate_for_enabled_user_id(data.get("enabled", 0))
self.validate_duplicate_user_id()
@@ -336,4 +336,4 @@ def get_children(doctype, parent=None, company=None, is_root=False, is_tree=Fals
.format(company=company, condition=condition), as_dict=1)
# return employee
return employee
return employee

View File

@@ -58,10 +58,9 @@ class EmployeeAdvance(Document):
select sum(ifnull(allocated_amount, 0))
from `tabExpense Claim Advance`
where employee_advance = %s and docstatus=1 and allocated_amount > 0
""", self.name)[0][0]
""", self.name)[0][0] or 0
if claimed_amount:
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", claimed_amount)
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", claimed_amount)
@frappe.whitelist()
def make_bank_entry(dt, dn):

View File

@@ -506,4 +506,5 @@ erpnext.patches.v10_0.update_address_template_for_india
erpnext.patches.v10_0.set_discount_amount
erpnext.patches.v10_0.recalculate_gross_margin_for_project
erpnext.patches.v10_0.delete_hub_documents
erpnext.patches.v10_0.update_user_image_in_employee
erpnext.patches.v10_0.update_user_image_in_employee
erpnext.patches.v10_0.repost_gle_for_purchase_receipts_with_rejected_items

View File

@@ -0,0 +1,32 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, erpnext
def execute():
for company in frappe.get_all("Company"):
if not erpnext.is_perpetual_inventory_enabled(company.name):
continue
acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto") or "1900-01-01"
pr_with_rejected_warehouse = frappe.db.sql("""
select pr.name
from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
where pr.name = pr_item.parent
and pr.posting_date > %s
and pr.docstatus=1
and pr.company = %s
and pr_item.rejected_qty > 0
""", (acc_frozen_upto, company.name), as_dict=1)
for d in pr_with_rejected_warehouse:
doc = frappe.get_doc("Purchase Receipt", d.name)
doc.docstatus = 2
doc.make_gl_entries_on_cancel(repost_future_gle=False)
# update gl entries for submit state of PR
doc.docstatus = 1
doc.make_gl_entries(repost_future_gle=False)

View File

@@ -254,13 +254,13 @@ class Project(Document):
self.total_purchase_cost = total_purchase_cost and total_purchase_cost[0][0] or 0
def update_sales_amount(self):
total_sales_amount = frappe.db.sql("""select sum(base_grand_total)
total_sales_amount = frappe.db.sql("""select sum(base_net_total)
from `tabSales Order` where project = %s and docstatus=1""", self.name)
self.total_sales_amount = total_sales_amount and total_sales_amount[0][0] or 0
def update_billed_amount(self):
total_billed_amount = frappe.db.sql("""select sum(base_grand_total)
total_billed_amount = frappe.db.sql("""select sum(base_net_total)
from `tabSales Invoice` where project = %s and docstatus=1""", self.name)
self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0

View File

@@ -346,6 +346,11 @@ body[data-route="pos"] .btn-more {
body[data-route="pos"] .collapse-btn {
cursor: pointer;
}
@media (max-width: 767px) {
body[data-route="pos"] .page-actions {
max-width: 110px;
}
}
.price-info {
position: absolute;
left: 0;

View File

@@ -10,9 +10,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
+ flt(item.price_list_rate) * ( flt(item.margin_rate_or_amount) / 100);
} else {
item.rate_with_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount);
item.base_rate_with_margin = flt(item.rate_with_margin) * flt(this.frm.doc.conversion_rate);
}
item.base_rate_with_margin = flt(item.rate_with_margin) * flt(this.frm.doc.conversion_rate);
item.rate = flt(item.rate_with_margin , precision("rate", item));
if(item.discount_percentage){

View File

@@ -33,7 +33,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
item.margin_rate_or_amount = 0;
item.rate_with_margin = 0;
}
item.base_rate_with_margin = item.rate_with_margin * flt(frm.doc.conversion_rate);
cur_frm.cscript.set_gross_profit(item);
cur_frm.cscript.calculate_taxes_and_totals();

View File

@@ -97,6 +97,9 @@ erpnext.setup.slides_settings = [
if (!this.values.company_abbr) {
return false;
}
if (this.values.company_abbr.length > 5) {
return false;
}
return true;
}
},

View File

@@ -320,7 +320,7 @@ frappe.ui.form.ItemQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({
["attribute_value", "like", e.target.value + "%"]
],
fields: ["attribute_value"],
parent: "Item"
parent: "Item Attribute"
},
callback: function(r) {
if (r.message) {

View File

@@ -8,22 +8,49 @@ def validate_gstin_for_india(doc, method):
if not hasattr(doc, 'gstin'):
return
if doc.gstin:
doc.gstin = doc.gstin.upper()
if doc.gstin not in ["NA", "na"]:
p = re.compile("[0-9]{2}[a-zA-Z]{5}[0-9]{4}[a-zA-Z]{1}[1-9A-Za-z]{1}[Z]{1}[0-9a-zA-Z]{1}")
if not p.match(doc.gstin):
frappe.throw(_("Invalid GSTIN or Enter NA for Unregistered"))
doc.gstin = doc.gstin.upper().strip()
if not doc.gstin or doc.gstin == 'NA':
return
if len(doc.gstin) != 15:
frappe.throw(_("Invalid GSTIN! A GSTIN must have 15 characters."))
p = re.compile("^[0-9]{2}[A-Z]{4}[0-9A-Z]{1}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}[1-9A-Z]{1}[0-9A-Z]{1}$")
if not p.match(doc.gstin):
frappe.throw(_("Invalid GSTIN! The input you've entered doesn't match the format of GSTIN."))
validate_gstin_check_digit(doc.gstin)
if not doc.gst_state:
if doc.state in states:
doc.gst_state = doc.state
if not doc.state:
return
state = doc.state.lower()
states_lowercase = {s.lower():s for s in states}
if state in states_lowercase:
doc.gst_state = states_lowercase[state]
else:
return
if doc.gst_state:
doc.gst_state_number = state_numbers[doc.gst_state]
if doc.gstin and doc.gstin != "NA" and doc.gst_state_number != doc.gstin[:2]:
frappe.throw(_("First 2 digits of GSTIN should match with State number {0}")
.format(doc.gst_state_number))
doc.gst_state_number = state_numbers[doc.gst_state]
if doc.gst_state_number != doc.gstin[:2]:
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
.format(doc.gst_state_number))
def validate_gstin_check_digit(gstin):
''' Function to validate the check digit of the GSTIN.'''
factor = 1
total = 0
code_point_chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
mod = len(code_point_chars)
input_chars = gstin[:-1]
for char in input_chars:
digit = factor * code_point_chars.find(char)
digit = (digit // mod) + (digit % mod)
total += digit
factor = 2 if factor == 1 else 1
if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]:
frappe.throw(_("Invalid GSTIN! The check digit validation has failed. " +
"Please ensure you've typed the GSTIN correctly."))
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
if frappe.get_meta(item_doctype).has_field('gst_hsn_code'):

View File

@@ -1,6 +1,11 @@
Selling management module. Includes forms for capturing / managing the sales process.
Selling management module. Includes forms for capturing / managing the sales process:
- Customer
- Campaign
- Quotation
- Sales Order
Moved to CRM Module:
- Lead
- Opportunity
- Quotation
- Sales Order

View File

@@ -173,6 +173,11 @@ class Customer(TransactionBase):
frappe.throw(_("""New credit limit is less than current outstanding amount for the customer. Credit limit has to be atleast {0}""").format(outstanding_amt))
def on_trash(self):
if self.customer_primary_contact:
frappe.db.sql("""update `tabCustomer`
set customer_primary_contact=null, mobile_no=null, email_id=null
where name=%s""", self.name)
delete_contact_and_address('Customer', self.name)
if self.lead_name:
frappe.db.sql("update `tabLead` set status='Interested' where name=%s", self.lead_name)

View File

@@ -96,6 +96,15 @@ class TestCustomer(unittest.TestCase):
so.save()
def test_delete_customer_contact(self):
customer = frappe.get_doc(
get_customer_dict('_Test Customer for delete')).insert(ignore_permissions=True)
customer.mobile_no = "8989889890"
customer.save()
self.assertTrue(customer.customer_primary_contact)
frappe.delete_doc('Customer', customer.name)
def test_disabled_customer(self):
make_test_records("Item")

View File

@@ -15,6 +15,7 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -44,11 +45,13 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,10 +77,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,10 +107,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -134,11 +141,13 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -165,10 +174,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -197,11 +208,13 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "300px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -227,10 +240,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -257,10 +272,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -288,10 +305,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -317,10 +336,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -349,11 +370,13 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -383,11 +406,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -412,10 +437,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -443,10 +470,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -473,10 +502,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -503,10 +534,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -532,10 +565,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -565,11 +600,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -599,11 +636,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -630,10 +669,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -662,10 +703,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -693,10 +736,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -725,10 +770,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -754,10 +801,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -787,11 +836,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -820,10 +871,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -852,10 +905,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -880,10 +935,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -915,11 +972,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -936,6 +995,7 @@
"label": "Net Rate",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
@@ -946,10 +1006,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -979,11 +1041,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1011,10 +1075,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1039,10 +1105,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1072,11 +1140,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1103,10 +1173,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1136,11 +1208,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1168,10 +1242,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1198,10 +1274,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1228,10 +1306,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1258,10 +1338,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1288,10 +1370,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1317,10 +1401,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1348,10 +1434,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1379,10 +1467,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1410,10 +1500,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1439,10 +1531,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1469,10 +1563,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1500,10 +1596,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1530,10 +1628,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1559,10 +1659,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1592,11 +1694,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1626,11 +1730,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1658,10 +1764,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1686,10 +1794,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -1717,10 +1827,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1750,10 +1862,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1783,6 +1897,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "150px"
}
@@ -1798,7 +1913,7 @@
"istable": 1,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-08-06 05:18:38.135668",
"modified": "2018-12-12 05:52:46.135944",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation Item",
@@ -1811,5 +1926,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
"track_seen": 0,
"track_views": 0
}

View File

@@ -552,7 +552,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
def update_item(source, target, source_parent):
target.amount = flt(source.amount) - flt(source.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate)
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty - source.returned_qty
item = frappe.db.get_value("Item", target.item_code, ["item_group", "selling_cost_center"], as_dict=1)
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \

View File

@@ -95,7 +95,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None):
if not value:
import requests
api_url = "https://frankfurter.erpnext.org/{0}".format(transaction_date)
api_url = "https://frankfurter.app/{0}".format(transaction_date)
response = requests.get(api_url, params={
"base": from_currency,
"symbols": to_currency

View File

@@ -382,8 +382,24 @@ def get_invoiced_qty_map(delivery_note):
return invoiced_qty_map
def get_returned_qty_map(sales_orders):
"""returns a map: {so_detail: returned_qty}"""
returned_qty_map = {}
for name, returned_qty in frappe.get_all('Sales Order Item', fields = ["name", "returned_qty"],
filters = {'parent': ('in', sales_orders), 'docstatus': 1}, as_list=1):
if not returned_qty_map.get(name):
returned_qty_map[name] = 0
returned_qty_map[name] += returned_qty
return returned_qty_map
@frappe.whitelist()
def make_sales_invoice(source_name, target_doc=None):
doc = frappe.get_doc('Delivery Note', source_name)
sales_orders = [d.against_sales_order for d in doc.items]
returned_qty_map = get_returned_qty_map(sales_orders)
invoiced_qty_map = get_invoiced_qty_map(source_name)
def set_missing_values(source, target):
@@ -403,7 +419,9 @@ def make_sales_invoice(source_name, target_doc=None):
target.update(get_fetch_values("Sales Invoice", 'company_address', target.company_address))
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = source_doc.qty - invoiced_qty_map.get(source_doc.name, 0)
target_doc.qty = (source_doc.qty -
invoiced_qty_map.get(source_doc.name, 0) - returned_qty_map.get(source_doc.so_detail, 0))
if source_doc.serial_no and source_parent.per_billed > 0:
target_doc.serial_no = get_delivery_note_serial_no(source_doc.item_code,
target_doc.qty, source_parent.name)

View File

@@ -1,6 +1,6 @@
frappe.listview_settings['Delivery Note'] = {
add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed",
"transporter_name", "grand_total", "is_return", "status"],
add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed",
"transporter_name", "grand_total", "is_return", "status", "currency"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];

View File

@@ -564,6 +564,24 @@ class TestDeliveryNote(unittest.TestCase):
self.assertEqual(dn.per_billed, 100)
self.assertEqual(dn.status, "Completed")
def test_make_sales_invoice_from_dn_for_returned_qty(self):
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
so = make_sales_order(qty=2)
so.submit()
dn = make_delivery_note(so.name)
dn.submit()
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-1, do_not_submit=True)
dn1.items[0].against_sales_order = so.name
dn1.items[0].so_detail = so.items[0].name
dn1.submit()
si = make_sales_invoice(dn.name)
self.assertEquals(si.items[0].qty, 1)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
args = frappe._dict(args)

View File

@@ -458,7 +458,7 @@ $.extend(erpnext.item, {
fields: ["attribute_value"],
limit_start: 0,
limit_page_length: 500,
parent: "Item"
parent: "Item Attribute"
}
}).then((r) => {
if(r.message) {
@@ -579,7 +579,7 @@ $.extend(erpnext.item, {
["attribute_value", "like", term + "%"]
],
fields: ["attribute_value"],
parent: "Item"
parent: "Item Attribute"
},
callback: function(r) {
if (r.message) {

View File

@@ -49,8 +49,9 @@ def update_packing_list_item(doc, packing_item_code, qty, main_item_row, descrip
pi.qty = flt(qty)
pi.description = description
if not pi.warehouse:
pi.warehouse = (main_item_row.warehouse
if (doc.get('is_pos') or not item.default_warehouse) else item.default_warehouse)
pi.warehouse = (main_item_row.warehouse if ((doc.get('is_pos')
or not item.default_warehouse) and main_item_row.warehouse) else item.default_warehouse)
if not pi.batch_no:
pi.batch_no = cstr(main_item_row.get("batch_no"))
if not pi.target_warehouse:

View File

@@ -182,7 +182,8 @@ class PurchaseReceipt(BuyingController):
if warehouse_account.get(d.warehouse):
stock_value_diff = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": self.name,
"voucher_detail_no": d.name}, "stock_value_difference")
"voucher_detail_no": d.name, "warehouse": d.warehouse}, "stock_value_difference")
if not stock_value_diff:
continue
gl_entries.append(self.get_gl_dict({

View File

@@ -1,6 +1,6 @@
frappe.listview_settings['Purchase Receipt'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted",
"transporter_name", "is_return", "status", "per_billed"],
"transporter_name", "is_return", "status", "per_billed", "currency"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];

View File

@@ -325,7 +325,8 @@ def make_purchase_receipt(**args):
"conversion_factor": args.conversion_factor or 1.0,
"serial_no": args.serial_no,
"stock_uom": args.stock_uom or "_Test UOM",
"uom": args.uom or "_Test UOM"
"uom": args.uom or "_Test UOM",
"cost_center": "_Test Cost Center - _TC"
})
if not args.do_not_save:

View File

@@ -81,9 +81,9 @@ class SerialNo(StockController):
self.purchase_date = purchase_sle.posting_date
self.purchase_time = purchase_sle.posting_time
self.purchase_rate = purchase_sle.incoming_rate
if purchase_sle.voucher_type == "Purchase Receipt":
if purchase_sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
self.supplier, self.supplier_name = \
frappe.db.get_value("Purchase Receipt", purchase_sle.voucher_no,
frappe.db.get_value(purchase_sle.voucher_type, purchase_sle.voucher_no,
["supplier", "supplier_name"])
# If sales return entry
@@ -253,8 +253,8 @@ def has_duplicate_serial_no(sn, sle):
status = False
if sn.purchase_document_no:
if sle.voucher_type in ['Purchase Receipt', 'Stock Entry'] and \
sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry']:
if sle.voucher_type in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"] and \
sn.delivery_document_type not in ['Purchase Receipt', 'Stock Entry', "Purchase Invoice"]:
status = True
if status and sle.voucher_type == 'Stock Entry' and \

View File

@@ -180,7 +180,7 @@ class StockReconciliation(StockController):
frappe.throw(_("Valuation Rate required for Item in row {0}").format(row.idx))
if ((previous_sle and row.qty == previous_sle.get("qty_after_transaction")
and row.valuation_rate == previous_sle.get("valuation_rate"))
and (row.valuation_rate == previous_sle.get("valuation_rate") or row.qty == 0))
or (not previous_sle and not row.qty)):
continue
@@ -270,24 +270,27 @@ class StockReconciliation(StockController):
@frappe.whitelist()
def get_items(warehouse, posting_date, posting_time):
items = frappe.get_list("Bin", fields=["item_code"], filters={"warehouse": warehouse}, as_list=1)
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])
items = frappe.db.sql("""select item_code, warehouse from tabBin
where exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=`tabBin`.warehouse)
""", (lft, rgt))
items += frappe.get_list("Item", fields=["name"], filters= {"is_stock_item": 1, "has_serial_no": 0,
"has_batch_no": 0, "has_variants": 0, "disabled": 0, "default_warehouse": warehouse},
as_list=1)
items += frappe.db.sql("""select name, default_warehouse from tabItem
where exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=`tabItem`.default_warehouse)
and is_stock_item = 1 and has_serial_no = 0 and has_batch_no = 0 and has_variants = 0 and disabled = 0
""", (lft, rgt))
res = []
for item in set(items):
stock_bal = get_stock_balance(item[0], warehouse, posting_date, posting_time,
for item, wh in set(items):
stock_bal = get_stock_balance(item, wh, posting_date, posting_time,
with_valuation_rate=True)
if frappe.db.get_value("Item",item[0],"disabled") == 0:
if frappe.db.get_value("Item", item, "disabled") == 0:
res.append({
"item_code": item[0],
"warehouse": warehouse,
"item_code": item,
"warehouse": wh,
"qty": stock_bal[0],
"item_name": frappe.db.get_value('Item', item[0], 'item_name'),
"item_name": frappe.db.get_value('Item', item, 'item_name'),
"valuation_rate": stock_bal[1],
"current_qty": stock_bal[0],
"current_valuation_rate": stock_bal[1]

View File

@@ -10,7 +10,9 @@ from frappe.utils import flt, nowdate, nowtime
from erpnext.accounts.utils import get_stock_and_account_difference
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.doctype.item.test_item import make_item
class TestStockReconciliation(unittest.TestCase):
def setUp(self):
@@ -79,6 +81,17 @@ class TestStockReconciliation(unittest.TestCase):
set_perpetual_inventory(0)
def test_get_items(self):
create_warehouse("_Test Warehouse Group 1", {"is_group": 1})
create_warehouse("_Test Warehouse Ledger 1", {"is_group": 0, "parent_warehouse": "_Test Warehouse Group 1 - _TC"})
make_item("_Test Stock Reco Item", {"default_warehouse": "_Test Warehouse Ledger 1 - _TC",
"is_stock_item": 1, "opening_stock": 100, "valuation_rate": 100})
items = get_items("_Test Warehouse Group 1 - _TC", nowdate(), nowtime())
self.assertEqual(["_Test Stock Reco Item", "_Test Warehouse Ledger 1 - _TC", 100],
[items[0]["item_code"], items[0]["warehouse"], items[0]["qty"]])
def insert_existing_sle(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry

View File

@@ -90,7 +90,7 @@ class TestWarehouse(unittest.TestCase):
self.assertTrue(frappe.db.get_value("Warehouse",
filters={"account": "Test Warehouse for Merging 2 - _TC"}))
def create_warehouse(warehouse_name):
def create_warehouse(warehouse_name, properties=None):
if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"):
w = frappe.new_doc("Warehouse")
w.warehouse_name = warehouse_name
@@ -98,11 +98,13 @@ def create_warehouse(warehouse_name):
w.company = "_Test Company"
make_account_for_warehouse(warehouse_name, w)
w.account = warehouse_name + " - _TC"
if properties:
w.update(properties)
w.save()
def make_account_for_warehouse(warehouse_name, warehouse_obj):
if not frappe.db.exists("Account", warehouse_name + " - _TC"):
parent_account = frappe.db.get_value('Account',
parent_account = frappe.db.get_value('Account',
{'company': warehouse_obj.company, 'is_group':1, 'account_type': 'Stock'},'name')
account = create_account(account_name=warehouse_name, \
account_type="Stock", parent_account= parent_account, company=warehouse_obj.company)

View File

@@ -176,7 +176,11 @@ class update_entries_after(object):
# rounding as per precision
self.stock_value = flt(self.stock_value, self.precision)
stock_value_difference = self.stock_value - self.prev_stock_value
if self.prev_stock_value < 0 and self.stock_value >= 0 and sle.voucher_type != 'Stock Reconciliation':
stock_value_difference = sle.actual_qty * self.valuation_rate
else:
stock_value_difference = self.stock_value - self.prev_stock_value
self.prev_stock_value = self.stock_value
# update current sle