Merge pull request #2 from frappe/hotfix

Hotfix
This commit is contained in:
Himanshu 2019-01-03 12:47:03 +05:30 committed by GitHub
commit a09a6e4020
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 501 additions and 149 deletions

View File

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

View File

@ -791,15 +791,25 @@ frappe.ui.form.on('Payment Entry', {
var write_off_row = $.map(frm.doc["deductions"] || [], function(t) {
return t.account==r.message[account] ? t : null; });
if (!write_off_row.length) {
var row = frm.add_child("deductions");
var row = [];
var difference_amount = flt(frm.doc.difference_amount,
precision("difference_amount"));
if (!write_off_row.length && difference_amount) {
row = frm.add_child("deductions");
row.account = r.message[account];
row.cost_center = r.message["cost_center"];
} else {
var row = write_off_row[0];
row = write_off_row[0];
}
if (row) {
row.amount = flt(row.amount) + difference_amount;
} else {
frappe.msgprint(__("No gain or loss in the exchange rate"))
}
row.amount = flt(row.amount) + flt(frm.doc.difference_amount);
refresh_field("deductions");
frm.events.set_unallocated_amount(frm);

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()
@ -671,9 +668,11 @@ class SalesInvoice(SellingController):
self.get_gl_dict({
"account": tax.account_head,
"against": self.customer,
"credit": flt(tax.base_tax_amount_after_discount_amount),
"credit_in_account_currency": flt(tax.base_tax_amount_after_discount_amount) \
if account_currency==self.company_currency else flt(tax.tax_amount_after_discount_amount),
"credit": flt(tax.base_tax_amount_after_discount_amount,
tax.precision("tax_amount_after_discount_amount")),
"credit_in_account_currency": (flt(tax.base_tax_amount_after_discount_amount,
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
"cost_center": tax.cost_center
}, account_currency)
)
@ -681,7 +680,7 @@ class SalesInvoice(SellingController):
def make_item_gl_entries(self, gl_entries):
# income account gl entries
for item in self.get("items"):
if flt(item.base_net_amount):
if flt(item.base_net_amount, item.precision("base_net_amount")):
if item.is_fixed_asset:
asset = frappe.get_doc("Asset", item.asset)
@ -698,9 +697,10 @@ class SalesInvoice(SellingController):
self.get_gl_dict({
"account": item.income_account,
"against": self.customer,
"credit": item.base_net_amount,
"credit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
if account_currency==self.company_currency
else flt(item.net_amount, item.precision("net_amount"))),
"cost_center": item.cost_center
}, account_currency)
)
@ -771,7 +771,7 @@ class SalesInvoice(SellingController):
def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos
if self.write_off_account and self.write_off_amount:
if self.write_off_account and flt(self.write_off_amount, self.precision("write_off_amount")):
write_off_account_currency = get_account_currency(self.write_off_account)
default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
@ -781,9 +781,10 @@ class SalesInvoice(SellingController):
"party_type": "Customer",
"party": self.customer,
"against": self.write_off_account,
"credit": self.base_write_off_amount,
"credit_in_account_currency": self.base_write_off_amount \
if self.party_account_currency==self.company_currency else self.write_off_amount,
"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"credit_in_account_currency": (flt(self.base_write_off_amount,
self.precision("base_write_off_amount")) if self.party_account_currency==self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))),
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype
}, self.party_account_currency)
@ -792,15 +793,16 @@ class SalesInvoice(SellingController):
self.get_gl_dict({
"account": self.write_off_account,
"against": self.customer,
"debit": self.base_write_off_amount,
"debit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"debit_in_account_currency": (flt(self.base_write_off_amount,
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))),
"cost_center": self.write_off_cost_center or default_cost_center
}, write_off_account_currency)
)
def make_gle_for_rounding_adjustment(self, gl_entries):
if self.rounding_adjustment:
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)
@ -808,8 +810,10 @@ class SalesInvoice(SellingController):
self.get_gl_dict({
"account": round_off_account,
"against": self.customer,
"credit_in_account_currency": self.rounding_adjustment,
"credit": self.base_rounding_adjustment,
"credit_in_account_currency": flt(self.rounding_adjustment,
self.precision("rounding_adjustment")),
"credit": flt(self.base_rounding_adjustment,
self.precision("base_rounding_adjustment")),
"cost_center": round_off_cost_center,
}
))

View File

@ -75,7 +75,7 @@
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
<td class="text-right">
{%= format_currency(flt(balance_row[__("Outstanding Amount")]), data[data.length-1]["currency"]) %}
{%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %}
</td>
</tr>
<td>{%= __("PDC/LC") %}</td>
@ -84,7 +84,7 @@
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[__("PDC/LC Amount")]), data[data.length-1]["currency"]) %}
{%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}
</td>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
@ -93,7 +93,7 @@
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row[__("Outstanding Amount")]-balance_row[__("PDC/LC Amount")]), data[data.length-1]["currency"]) %}</th>
{%= format_currency(flt(balance_row[("outstanding_amount")]-balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
</tbody>
@ -177,10 +177,10 @@
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("Remaining Balance")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][("pdc/lc_date")]) %}</td>
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td></td>
@ -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>
@ -205,9 +205,9 @@
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= frappe.datetime.str_to_user(data[i][__("PDC/LC Date")]) %}</td>
<td style="text-align: right">{%= data[i][__("PDC/LC Ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("PDC/LC Amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][__("Remaining Balance")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
@ -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")] %}
@ -228,10 +228,10 @@
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
{% } %}
<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">{%= format_currency(data[i][__("Total Outstanding Amt")], data[i]["currency"]) %}</td>
<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">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
</tr>

View File

@ -88,20 +88,40 @@ class ReceivablePayableReport(object):
"width": 120
})
columns.append({
columns += [
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"width": 100
})
columns += [
_("PDC/LC Date") + ":Date:110",
_("PDC/LC Ref") + ":Data:110",
_("PDC/LC Amount") + ":Currency/currency:130",
_("Remaining Balance") + ":Currency/currency:130"
]
},
{
"fieldname": "pdc/lc_date",
"label": _("PDC/LC Date"),
"fieldtype": "Date",
"width": 110
},
{
"fieldname": "pdc/lc_ref",
"label": _("PDC/LC Ref"),
"fieldtype": "Data",
"width": 110
},
{
"fieldname": "pdc/lc_amount",
"label": _("PDC/LC Amount"),
"fieldtype": "Currency",
"options": "Currency",
"width": 130
},
{
"fieldname": "remaining_balance",
"label": _("Remaining Balance"),
"fieldtype": "Currency",
"options": "Currency",
"width": 130
}]
if args.get('party_type') == 'Customer':
columns.append({
@ -139,7 +159,6 @@ class ReceivablePayableReport(object):
data = []
pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date)
gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type"))
if gl_entries_data:
@ -429,7 +448,6 @@ def get_pdc_details(party_type, report_date):
and pent.party_type = %s
group by pent.party, pref.reference_name""", (report_date, party_type), as_dict=1):
pdc_details.setdefault((pdc.invoice_no, pdc.party), pdc)
if scrub(party_type):
amount_field = ("jea.debit_in_account_currency"
if party_type == 'Supplier' else "jea.credit_in_account_currency")

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:
@ -54,7 +54,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
]
row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount] \
if d.stock_uom != d.uom else [d.base_net_rate, d.base_net_amount]
if d.stock_uom != d.uom and d.stock_qty != 0 else [d.base_net_rate, d.base_net_amount]
total_tax = 0
for tax in tax_columns:

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

@ -14,10 +14,6 @@
margin-bottom: -4px;
}
.medical_record-row > * {
z-index: -999;
}
.date-indicator {
background:none;
font-size:12px;

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,7 +58,7 @@ 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
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", claimed_amount)

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

@ -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

@ -15,19 +15,24 @@ class SMSCenter(Document):
def create_receiver_list(self):
rec, where_clause = '', ''
if self.send_to == 'All Customer Contact':
where_clause = self.customer and " and customer = '%s'" % \
self.customer.replace("'", "\'") or " and ifnull(customer, '') != ''"
where_clause = " and dl.link_doctype = 'Customer'"
if self.customer:
where_clause += " and dl.link_name = '%s'" % \
self.customer.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to == 'All Supplier Contact':
where_clause = self.supplier and " and supplier = '%s'" % \
self.supplier.replace("'", "\'") or " and ifnull(supplier, '') != ''"
where_clause = " and dl.link_doctype = 'Supplier'"
if self.supplier:
where_clause += " and dl.link_name = '%s'" % \
self.supplier.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to == 'All Sales Partner Contact':
where_clause = self.sales_partner and " and sales_partner = '%s'" % \
self.sales_partner.replace("'", "\'") or " and ifnull(sales_partner, '') != ''"
where_clause = " and dl.link_doctype = 'Sales Partner'"
if self.sales_partner:
where_clause += "and dl.link_name = '%s'" % \
self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''"
if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']:
rec = frappe.db.sql("""select CONCAT(ifnull(first_name,''), ' ', ifnull(last_name,'')),
mobile_no from `tabContact` where ifnull(mobile_no,'')!='' and
docstatus != 2 %s""" % where_clause)
rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')),
c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and
c.docstatus != 2 and dl.parent = c.name%s""" % where_clause)
elif self.send_to == 'All Lead (Open)':
rec = frappe.db.sql("""select lead_name, mobile_no from `tabLead` where
@ -50,7 +55,6 @@ class SMSCenter(Document):
where ifnull(tabEmployee.cell_number,'')!=''""")
rec_list = ''
for d in rec:
rec_list += d[0] + ' - ' + d[1] + '\n'
self.receiver_list = rec_list

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

@ -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

View File

@ -84,6 +84,14 @@ def get_suppliers_details(filters):
is_stock_item=1 and name=pri.item_code)""", as_dict=1):
item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
for d in frappe.db.sql("""select pr.supplier, pri.item_code from
`tabPurchase Invoice` pr, `tabPurchase Invoice Item` pri
where pr.name=pri.parent and pr.docstatus=1 and
ifnull(pr.update_stock, 0) = 1 and pri.item_code=(select name from `tabItem`
where is_stock_item=1 and name=pri.item_code)""", as_dict=1):
if d.item_code not in item_supplier_map:
item_supplier_map.setdefault(d.item_code, []).append(d.supplier)
if supplier:
for item_code, suppliers in item_supplier_map.items():
if supplier not in suppliers:

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