Merge branch 'develop' into multi-barcode

This commit is contained in:
tundebabzy 2018-01-10 21:49:27 +01:00 committed by GitHub
commit 76dddbf26e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
414 changed files with 105304 additions and 77387 deletions

BIN
.swp Normal file

Binary file not shown.

View File

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

View File

@ -53,14 +53,12 @@ class Account(NestedSet):
def set_root_and_report_type(self): def set_root_and_report_type(self):
if self.parent_account: if self.parent_account:
par = frappe.db.get_value("Account", self.parent_account, par = frappe.db.get_value("Account", self.parent_account,
["report_type", "root_type", "account_type"], as_dict=1) ["report_type", "root_type"], as_dict=1)
if par.report_type: if par.report_type:
self.report_type = par.report_type self.report_type = par.report_type
if par.root_type: if par.root_type:
self.root_type = par.root_type self.root_type = par.root_type
if par.account_type and not self.account_type:
self.account_type = par.account_type
if self.is_group: if self.is_group:
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1) db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
@ -165,16 +163,16 @@ class Account(NestedSet):
if self.check_gle_exists(): if self.check_gle_exists():
throw(_("Account with existing transaction can not be deleted")) throw(_("Account with existing transaction can not be deleted"))
super(Account, self).on_trash() super(Account, self).on_trash(True)
def before_rename(self, old, new, merge=False): def before_rename(self, old, new, merge=False):
# Add company abbr if not provided # Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr from erpnext.setup.doctype.company.company import get_name_with_abbr
new_account = get_name_with_abbr(new, self.company) new_account = get_name_with_abbr(new, self.company)
new_account = get_name_with_number(new_account, self.account_number) if not merge:
new_account = get_name_with_number(new_account, self.account_number)
# Validate properties before merging else:
if merge: # Validate properties before merging
if not frappe.db.exists("Account", new): if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new)) throw(_("Account {0} does not exist").format(new))

View File

@ -79,15 +79,17 @@ frappe.treeview_settings["Account"] = {
}, },
onrender: function(node) { onrender: function(node) {
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
if (node.data && node.data.balance!==undefined) { var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
$('<span class="balance-area pull-right text-muted small">' if (node.data && node.data.balance!==undefined) {
+ (node.data.balance_in_account_currency ? $('<span class="balance-area pull-right text-muted small">'
(format_currency(Math.abs(node.data.balance_in_account_currency), + (node.data.balance_in_account_currency ?
node.data.account_currency) + " / ") : "") (format_currency(Math.abs(node.data.balance_in_account_currency),
+ format_currency(Math.abs(node.data.balance), node.data.company_currency) node.data.account_currency) + " / ") : "")
+ " " + dr_or_cr + format_currency(Math.abs(node.data.balance), node.data.company_currency)
+ '</span>').insertBefore(node.$ul); + " " + dr_or_cr
+ '</span>').insertBefore(node.$ul);
}
} }
}, },
toolbar: [ toolbar: [

View File

@ -52,6 +52,7 @@ def _make_test_records(verbose):
["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None], ["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None], ["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None], ["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
["_Test Employee Advance", "Current Liabilities", 0, None, None],
["_Test Account Tax Assets", "Current Assets", 1, None, None], ["_Test Account Tax Assets", "Current Assets", 1, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None], ["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],

View File

@ -287,6 +287,125 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "print_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Print Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "show_inclusive_tax_in_print",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Show Inclusive Tax In Print",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_12",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "show_payment_schedule_in_print",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Show Payment Schedule in Print",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -392,7 +511,7 @@
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-09-05 10:10:03.117505", "modified": "2018-01-05 15:26:10.357085",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",
@ -417,7 +536,7 @@
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0, "apply_user_permissions": 0,
@ -437,7 +556,7 @@
"share": 0, "share": 0,
"submit": 0, "submit": 0,
"write": 0 "write": 0
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0, "apply_user_permissions": 0,
@ -466,4 +585,4 @@
"sort_order": "ASC", "sort_order": "ASC",
"track_changes": 1, "track_changes": 1,
"track_seen": 0 "track_seen": 0
} }

View File

@ -7,6 +7,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint from frappe.utils import cint
from frappe.model.document import Document from frappe.model.document import Document
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
class AccountsSettings(Document): class AccountsSettings(Document):
@ -15,6 +16,7 @@ class AccountsSettings(Document):
def validate(self): def validate(self):
self.validate_stale_days() self.validate_stale_days()
self.enable_payment_schedule_in_print()
def validate_stale_days(self): def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0: if not self.allow_stale and cint(self.stale_days) <= 0:
@ -22,3 +24,8 @@ class AccountsSettings(Document):
"Stale Days should start from 1.", title='Error', indicator='red', "Stale Days should start from 1.", title='Error', indicator='red',
raise_exception=1) raise_exception=1)
def enable_payment_schedule_in_print(self):
show_in_print = cint(self.show_payment_schedule_in_print)
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check")
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check")

View File

@ -74,36 +74,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "due_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Due Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -748,7 +718,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-08-10 18:06:44.904081", "modified": "2017-12-20 12:40:09.611951",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GL Entry", "name": "GL Entry",
@ -824,4 +794,4 @@
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 0, "track_changes": 0,
"track_seen": 0 "track_seen": 0
} }

View File

@ -0,0 +1,196 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-01-02 15:48:58.768352",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cgst_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "CGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sgst_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "SGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "igst_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "IGST Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cess_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "CESS Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2018-01-02 15:52:22.335988",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GST Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class GSTAccount(Document):
pass

View File

@ -124,6 +124,15 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
}; };
} }
if(jvd.reference_type==="Employee Advance") {
return {
filters: {
'status': ['=', 'Unpaid'],
'docstatus': 1
}
};
}
// journal entry // journal entry
if(jvd.reference_type==="Journal Entry") { if(jvd.reference_type==="Journal Entry") {
frappe.model.validate_missing(jvd, "account"); frappe.model.validate_missing(jvd, "account");
@ -185,56 +194,21 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
}) })
}, },
due_date_options_cache: {},
reference_name: function(doc, cdt, cdn) { reference_name: function(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn); var d = frappe.get_doc(cdt, cdn);
var me = this;
const get_invoice_due_dates = invoice_name => {
const options = this.due_date_options_cache[invoice_name];
const input = $(cur_frm.fields_dict["accounts"].wrapper).find("select[data-fieldname=reference_due_date]");
if (options) {
input.empty();
input.add_options(options);
frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
}
else {
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_invoice_due_dates",
args: {name: invoice_name},
callback: function(r) {
const options = [];
$.each(r.message, function(key, value) {
options.push(value.due_date);
});
input.empty();
input.add_options(options);
frappe.model.set_value(cdt, cdn, "reference_due_date", options[0]);
me.due_date_options_cache[d.reference_name] = options;
}
});
}
}
if(d.reference_name) { if(d.reference_name) {
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d); this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
} } else if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d); this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d);
} } else if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d); this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
} }
if( in_list(["Sales Invoice", "Purchase Invoice"]), d.reference_type) {
get_invoice_due_dates(d.reference_name);
}
} }
}, },
get_outstanding: function(doctype, docname, company, child) { get_outstanding: function(doctype, docname, company, child, due_date) {
var me = this; var me = this;
var args = { var args = {
"doctype": doctype, "doctype": doctype,

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext, json import frappe, erpnext, json
from frappe.utils import cstr, flt, fmt_money, formatdate from frappe.utils import cstr, flt, fmt_money, formatdate, getdate
from frappe import msgprint, _, scrub from frappe import msgprint, _, scrub
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on, get_account_currency from erpnext.accounts.utils import get_balance_on, get_account_currency
@ -54,8 +54,8 @@ class JournalEntry(AccountsController):
def update_advance_paid(self): def update_advance_paid(self):
advance_paid = frappe._dict() advance_paid = frappe._dict()
for d in self.get("accounts"): for d in self.get("accounts"):
if d.is_advance == "Yes": if d.is_advance:
if d.reference_type in ("Sales Order", "Purchase Order"): if d.reference_type in ("Sales Order", "Purchase Order", "Employee Advance"):
advance_paid.setdefault(d.reference_type, []).append(d.reference_name) advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
for voucher_type, order_list in advance_paid.items(): for voucher_type, order_list in advance_paid.items():
@ -101,8 +101,6 @@ class JournalEntry(AccountsController):
if account_type in ["Receivable", "Payable"]: if account_type in ["Receivable", "Payable"]:
if not (d.party_type and d.party): if not (d.party_type and d.party):
frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account)) frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account))
elif d.party_type and d.party:
frappe.throw(_("Row {0}: Party Type and Party is only applicable against Receivable / Payable account").format(d.idx))
def check_credit_limit(self): def check_credit_limit(self):
customers = list(set([d.party for d in self.get("accounts") customers = list(set([d.party for d in self.get("accounts")
@ -436,8 +434,7 @@ class JournalEntry(AccountsController):
"against_voucher": d.reference_name, "against_voucher": d.reference_name,
"remarks": self.remark, "remarks": self.remark,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"project": d.project, "project": d.project
"due_date": d.reference_due_date
}) })
) )
@ -649,9 +646,8 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
party_type = "Supplier" party_type = "Supplier"
party_account = ref_doc.credit_to party_account = ref_doc.credit_to
if (dt == "Sales Invoice" and ref_doc.outstanding_amount > 0) \
if (dt=="Sales Invoice" and ref_doc.outstanding_amount > 0) \ or (dt == "Purchase Invoice" and ref_doc.outstanding_amount < 0):
or (dt=="Purchase Invoice" and ref_doc.outstanding_amount < 0):
amount_field_party = "credit_in_account_currency" amount_field_party = "credit_in_account_currency"
amount_field_bank = "debit_in_account_currency" amount_field_bank = "debit_in_account_currency"
else: else:
@ -672,6 +668,7 @@ def get_payment_entry_against_invoice(dt, dn, amount=None, debit_in_account_cur
"journal_entry": journal_entry "journal_entry": journal_entry
}) })
def get_payment_entry(ref_doc, args): def get_payment_entry(ref_doc, args):
cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center") cost_center = frappe.db.get_value("Company", ref_doc.company, "cost_center")
exchange_rate = 1 exchange_rate = 1
@ -696,7 +693,7 @@ def get_payment_entry(ref_doc, args):
"cost_center": cost_center, "cost_center": cost_center,
"account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"), "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"),
"account_currency": args.get("party_account_currency") or \ "account_currency": args.get("party_account_currency") or \
get_account_currency(args.get("party_account")), get_account_currency(args.get("party_account")),
"balance": get_balance_on(args.get("party_account")), "balance": get_balance_on(args.get("party_account")),
"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")), "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
"exchange_rate": exchange_rate, "exchange_rate": exchange_rate,
@ -708,7 +705,7 @@ def get_payment_entry(ref_doc, args):
bank_row = je.append("accounts") bank_row = je.append("accounts")
#make it bank_details # Make it bank_details
bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account")) bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
if bank_account: if bank_account:
bank_row.update(bank_account) bank_row.update(bank_account)
@ -727,7 +724,7 @@ def get_payment_entry(ref_doc, args):
else: else:
bank_row.set(args.get("amount_field_bank"), amount * exchange_rate) bank_row.set(args.get("amount_field_bank"), amount * exchange_rate)
# set multi currency check # Multi currency check again
if party_row.account_currency != ref_doc.company_currency \ if party_row.account_currency != ref_doc.company_currency \
or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency): or (bank_row.account_currency and bank_row.account_currency != ref_doc.company_currency):
je.multi_currency = 1 je.multi_currency = 1
@ -737,6 +734,7 @@ def get_payment_entry(ref_doc, args):
return je if args.get("journal_entry") else je.as_dict() return je if args.get("journal_entry") else je.as_dict()
@frappe.whitelist() @frappe.whitelist()
def get_opening_accounts(company): def get_opening_accounts(company):
"""get all balance sheet accounts for opening entry""" """get all balance sheet accounts for opening entry"""
@ -759,6 +757,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(frappe.db.escape(searchfield)), and jv.docstatus = 1 and jv.`{0}` like %s order by jv.name desc limit %s, %s""".format(frappe.db.escape(searchfield)),
(filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len)) (filters.get("account"), cstr(filters.get("party")), "%{0}%".format(txt), start, page_len))
@frappe.whitelist() @frappe.whitelist()
def get_outstanding(args): def get_outstanding(args):
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
@ -820,6 +819,7 @@ def get_party_account_and_balance(company, party_type, party):
"account_currency": frappe.db.get_value("Account", account, "account_currency") "account_currency": frappe.db.get_value("Account", account, "account_currency")
} }
@frappe.whitelist() @frappe.whitelist()
def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None): def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" """Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
@ -857,7 +857,7 @@ def get_account_balance_and_party_type(account, date, company, debit=None, credi
return grid_values return grid_values
# Added posting_date as one of the parameters of get_exchange_rate
@frappe.whitelist() @frappe.whitelist()
def get_exchange_rate(posting_date, account=None, account_currency=None, company=None, def get_exchange_rate(posting_date, account=None, account_currency=None, company=None,
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None): reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
@ -890,6 +890,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company
# don't return None or 0 as it is multipled with a value and that value could be lost # don't return None or 0 as it is multipled with a value and that value could be lost
return exchange_rate or 1 return exchange_rate or 1
@frappe.whitelist() @frappe.whitelist()
def get_average_exchange_rate(account): def get_average_exchange_rate(account):
exchange_rate = 0 exchange_rate = 0
@ -898,15 +899,4 @@ def get_average_exchange_rate(account):
bank_balance_in_company_currency = get_balance_on(account, in_account_currency=False) bank_balance_in_company_currency = get_balance_on(account, in_account_currency=False)
exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency
return exchange_rate return exchange_rate
@frappe.whitelist()
def get_invoice_due_dates(name):
result = frappe.get_list(
doctype='GL Entry', group_by='name, due_date',
filters={'voucher_no': name, "ifnull(due_date, '')": ('!=', '')},
fields=['due_date'], distinct=True
)
return result

View File

@ -618,7 +618,7 @@
"label": "Reference Type", "label": "Reference Type",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nEmployee Loan\nPayroll Entry", "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nEmployee Loan\nPayroll Entry\nEmployee Advance",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -661,39 +661,7 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan'])",
"fieldname": "reference_due_date",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Due Date",
"length": 0,
"no_copy": 0,
"options": "",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -827,7 +795,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-06 19:54:19.851534", "modified": "2017-12-07 19:54:19.851534",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",

View File

@ -4,10 +4,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _, scrub
from frappe.utils import flt from frappe.utils import flt, nowdate
from frappe.model.document import Document from frappe.model.document import Document
class OpeningInvoiceCreationTool(Document): class OpeningInvoiceCreationTool(Document):
def onload(self): def onload(self):
"""Load the Opening Invoice summary""" """Load the Opening Invoice summary"""
@ -67,29 +68,25 @@ class OpeningInvoiceCreationTool(Document):
for row in self.invoices: for row in self.invoices:
if not row.qty: if not row.qty:
row.qty = 1.0 row.qty = 1.0
if not row.party:
frappe.throw(mandatory_error_msg.format(
idx=row.idx,
field= _("Party"),
invoice_type=self.invoice_type
))
# set party type if not available
if not row.party_type:
row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
# always mandatory fields for the invoices
if not row.temporary_opening_account:
row.temporary_opening_account = get_temporary_opening_account(self.company)
row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
if not row.item_name:
row.item_name = _("Opening Invoice Item")
if not row.posting_date: if not row.posting_date:
frappe.throw(mandatory_error_msg.format( row.posting_date = nowdate()
idx=row.idx, if not row.due_date:
field= _("Party"), row.due_date = nowdate()
invoice_type=self.invoice_type
))
if not row.outstanding_amount: for d in ("Party", "Outstanding Amount", "Temporary Opening Account"):
frappe.throw(mandatory_error_msg.format( if not row.get(scrub(d)):
idx=row.idx, frappe.throw(mandatory_error_msg.format(
field= _("Outstanding Amount"), idx=row.idx,
invoice_type=self.invoice_type field=_(d),
)) invoice_type=self.invoice_type
))
args = self.get_invoice_dict(row=row) args = self.get_invoice_dict(row=row)
if not args: if not args:
@ -99,10 +96,14 @@ class OpeningInvoiceCreationTool(Document):
doc.submit() doc.submit()
names.append(doc.name) names.append(doc.name)
if(len(self.invoices) > 5): if len(self.invoices) > 5:
frappe.publish_realtime("progress", frappe.publish_realtime(
dict(progress=[row.idx, len(self.invoices)], title=_('Creating {0}').format(doc.doctype)), "progress", dict(
user=frappe.session.user) progress=[row.idx, len(self.invoices)],
title=_('Creating {0}').format(doc.doctype)
),
user=frappe.session.user
)
return names return names
@ -111,8 +112,10 @@ class OpeningInvoiceCreationTool(Document):
default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos") default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
cost_center = frappe.db.get_value("Company", self.company, "cost_center") cost_center = frappe.db.get_value("Company", self.company, "cost_center")
if not cost_center: if not cost_center:
frappe.throw(_("Please set the Default Cost Center in {0} company").format(frappe.bold(self.company))) frappe.throw(
rate = flt(row.outstanding_amount) / row.qty _("Please set the Default Cost Center in {0} company").format(frappe.bold(self.company))
)
rate = flt(row.outstanding_amount) / flt(row.qty)
return frappe._dict({ return frappe._dict({
"uom": default_uom, "uom": default_uom,
@ -143,8 +146,7 @@ class OpeningInvoiceCreationTool(Document):
"due_date": row.due_date, "due_date": row.due_date,
"posting_date": row.posting_date, "posting_date": row.posting_date,
frappe.scrub(party_type): row.party, frappe.scrub(party_type): row.party,
"doctype": "Sales Invoice" if self.invoice_type == "Sales" \ "doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
else "Purchase Invoice",
"currency": frappe.db.get_value("Company", self.company, "default_currency") "currency": frappe.db.get_value("Company", self.company, "default_currency")
}) })
@ -160,4 +162,4 @@ def get_temporary_opening_account(company=None):
if not accounts: if not accounts:
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts")) frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
return accounts[0].name return accounts[0].name

View File

@ -161,7 +161,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -192,7 +192,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -284,6 +284,66 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "qty",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Quantity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
@ -300,7 +360,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-15 14:19:00.433148", "modified": "2017-12-19 05:07:01.549918",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Opening Invoice Creation Tool Item", "name": "Opening Invoice Creation Tool Item",

View File

@ -266,6 +266,7 @@ frappe.ui.form.on('Payment Entry', {
} }
}, },
() => frm.set_value("party_balance", r.message.party_balance), () => frm.set_value("party_balance", r.message.party_balance),
() => frm.set_value("party_name", r.message.party_name),
() => frm.events.get_outstanding_documents(frm), () => frm.events.get_outstanding_documents(frm),
() => frm.events.hide_unhide_fields(frm), () => frm.events.hide_unhide_fields(frm),
() => frm.events.set_dynamic_labels(frm), () => frm.events.set_dynamic_labels(frm),
@ -408,7 +409,7 @@ frappe.ui.form.on('Payment Entry', {
} }
// Make read only if Accounts Settings doesn't allow stale rates // Make read only if Accounts Settings doesn't allow stale rates
frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed()); frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
}, },
target_exchange_rate: function(frm) { target_exchange_rate: function(frm) {
@ -429,7 +430,7 @@ frappe.ui.form.on('Payment Entry', {
frm.set_paid_amount_based_on_received_amount = false; frm.set_paid_amount_based_on_received_amount = false;
// Make read only if Accounts Settings doesn't allow stale rates // Make read only if Accounts Settings doesn't allow stale rates
frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed()); frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed() ? 0 : 1);
}, },
paid_amount: function(frm) { paid_amount: function(frm) {

View File

@ -3,7 +3,7 @@
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, erpnext, json
from frappe import _, scrub, ValidationError from frappe import _, scrub, ValidationError
from frappe.utils import flt, comma_or, nowdate from frappe.utils import flt, comma_or, nowdate
from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on from erpnext.accounts.utils import get_outstanding_invoices, get_account_currency, get_balance_on
@ -71,9 +71,10 @@ class PaymentEntry(AccountsController):
def validate_duplicate_entry(self): def validate_duplicate_entry(self):
reference_names = [] reference_names = []
for d in self.get("references"): for d in self.get("references"):
if (d.reference_doctype, d.reference_name, d.due_date) in reference_names: if (d.reference_doctype, d.reference_name) in reference_names:
frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}").format(d.idx, d.reference_doctype, d.reference_name)) frappe.throw(_("Row #{0}: Duplicate entry in References {1} {2}")
reference_names.append((d.reference_doctype, d.reference_name, d.due_date)) .format(d.idx, d.reference_doctype, d.reference_name))
reference_names.append((d.reference_doctype, d.reference_name))
def validate_allocated_amount(self): def validate_allocated_amount(self):
for d in self.get("references"): for d in self.get("references"):
@ -147,7 +148,7 @@ class PaymentEntry(AccountsController):
if not frappe.db.exists(self.party_type, self.party): if not frappe.db.exists(self.party_type, self.party):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
if self.party_account: if self.party_account and self.party_type != "Employee":
party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable" party_account_type = "Receivable" if self.party_type in ("Customer", "Student") else "Payable"
self.validate_account_type(self.party_account, [party_account_type]) self.validate_account_type(self.party_account, [party_account_type])
@ -188,7 +189,7 @@ class PaymentEntry(AccountsController):
elif self.party_type == "Supplier": elif self.party_type == "Supplier":
valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry")
elif self.party_type == "Employee": elif self.party_type == "Employee":
valid_reference_doctypes = ("Expense Claim", "Journal Entry") valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance")
for d in self.get("references"): for d in self.get("references"):
if not d.allocated_amount: if not d.allocated_amount:
@ -205,7 +206,7 @@ class PaymentEntry(AccountsController):
if d.reference_doctype != "Journal Entry": if d.reference_doctype != "Journal Entry":
if self.party != ref_doc.get(scrub(self.party_type)): if self.party != ref_doc.get(scrub(self.party_type)):
frappe.throw(_("{0} {1} does not associated with {2} {3}") frappe.throw(_("{0} {1} is not associated with {2} {3}")
.format(d.reference_doctype, d.reference_name, self.party_type, self.party)) .format(d.reference_doctype, d.reference_name, self.party_type, self.party))
else: else:
self.validate_journal_entry() self.validate_journal_entry()
@ -413,8 +414,7 @@ class PaymentEntry(AccountsController):
gle = party_gl_dict.copy() gle = party_gl_dict.copy()
gle.update({ gle.update({
"against_voucher_type": d.reference_doctype, "against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name, "against_voucher": d.reference_name
"due_date": d.due_date
}) })
allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate), allocated_amount_in_company_currency = flt(flt(d.allocated_amount) * flt(d.exchange_rate),
@ -483,8 +483,9 @@ class PaymentEntry(AccountsController):
def update_advance_paid(self): def update_advance_paid(self):
if self.payment_type in ("Receive", "Pay") and self.party: if self.payment_type in ("Receive", "Pay") and self.party:
for d in self.get("references"): for d in self.get("references"):
if d.allocated_amount and d.reference_doctype in ("Sales Order", "Purchase Order"): if d.allocated_amount \
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() and d.reference_doctype in ("Sales Order", "Purchase Order", "Employee Advance"):
frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid()
def update_expense_claim(self): def update_expense_claim(self):
if self.payment_type in ("Pay") and self.party: if self.payment_type in ("Pay") and self.party:
@ -507,13 +508,18 @@ def get_outstanding_reference_documents(args):
# Get negative outstanding sales /purchase invoices # Get negative outstanding sales /purchase invoices
negative_outstanding_invoices = [] negative_outstanding_invoices = []
if args.get("party_type") not in ["Student", "Employee"]: if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
args.get("party"), args.get("party_account"), party_account_currency, company_currency) args.get("party"), args.get("party_account"), party_account_currency, company_currency)
# Get positive outstanding sales /purchase invoices/ Fees # Get positive outstanding sales /purchase invoices/ Fees
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
condition = " and voucher_type='{0}' and voucher_no='{1}'"\
.format(frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]))
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account")) args.get("party_account"), condition=condition)
for d in outstanding_invoices: for d in outstanding_invoices:
d["exchange_rate"] = 1 d["exchange_rate"] = 1
@ -535,6 +541,7 @@ def get_outstanding_reference_documents(args):
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency): def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency):
if party_type == "Customer": if party_type == "Customer":
voucher_type = 'Sales Order' voucher_type = 'Sales Order'
@ -563,18 +570,17 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
and abs(100 - per_billed) > 0.01 and abs(100 - per_billed) > 0.01
order by order by
transaction_date, name transaction_date, name
""".format(**{ """.format(**{
"ref_field": ref_field, "ref_field": ref_field,
"voucher_type": voucher_type, "voucher_type": voucher_type,
"party_type": scrub(party_type) "party_type": scrub(party_type)
}), party, as_dict = True) }), party, as_dict=True)
order_list = [] order_list = []
for d in orders: for d in orders:
d["voucher_type"] = voucher_type d["voucher_type"] = voucher_type
# This assumes that the exchange rate required is the one in the SO # This assumes that the exchange rate required is the one in the SO
d["exchange_rate"] = get_exchange_rate(party_account_currency, d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
company_currency, posting_date)
order_list.append(d) order_list.append(d)
return order_list return order_list
@ -608,6 +614,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
"party_account": "debit_to" if party_type == "Customer" else "credit_to" "party_account": "debit_to" if party_type == "Customer" else "credit_to"
}), (party, party_account), as_dict=True) }), (party, party_account), as_dict=True)
@frappe.whitelist() @frappe.whitelist()
def get_party_details(company, party_type, party, date): def get_party_details(company, party_type, party, date):
if not frappe.db.exists(party_type, party): if not frappe.db.exists(party_type, party):
@ -617,15 +624,19 @@ def get_party_details(company, party_type, party, date):
account_currency = get_account_currency(party_account) account_currency = get_account_currency(party_account)
account_balance = get_balance_on(party_account, date) account_balance = get_balance_on(party_account, date)
_party_name = "title" if party_type == "Student" else party_type.lower() + "_name"
party_name = frappe.db.get_value(party_type, party, _party_name)
party_balance = get_balance_on(party_type=party_type, party=party) party_balance = get_balance_on(party_type=party_type, party=party)
return { return {
"party_account": party_account, "party_account": party_account,
"party_name": party_name,
"party_account_currency": account_currency, "party_account_currency": account_currency,
"party_balance": party_balance, "party_balance": party_balance,
"account_balance": account_balance "account_balance": account_balance
} }
@frappe.whitelist() @frappe.whitelist()
def get_account_details(account, date): def get_account_details(account, date):
frappe.has_permission('Payment Entry', throw=True) frappe.has_permission('Payment Entry', throw=True)
@ -635,6 +646,7 @@ def get_account_details(account, date):
"account_type": frappe.db.get_value("Account", account, "account_type") "account_type": frappe.db.get_value("Account", account, "account_type")
}) })
@frappe.whitelist() @frappe.whitelist()
def get_company_defaults(company): def get_company_defaults(company):
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"] fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
@ -647,19 +659,23 @@ def get_company_defaults(company):
return ret return ret
@frappe.whitelist() @frappe.whitelist()
def get_reference_details(reference_doctype, reference_name, party_account_currency): def get_reference_details(reference_doctype, reference_name, party_account_currency):
total_amount = outstanding_amount = exchange_rate = None total_amount = outstanding_amount = exchange_rate = None
ref_doc = frappe.get_doc(reference_doctype, reference_name) ref_doc = frappe.get_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
if reference_doctype == "Fees": if reference_doctype == "Fees":
total_amount = ref_doc.get("grand_total") total_amount = ref_doc.get("grand_total")
exchange_rate = 1 exchange_rate = 1
outstanding_amount = ref_doc.get("outstanding_amount") outstanding_amount = ref_doc.get("outstanding_amount")
elif reference_doctype != "Journal Entry": elif reference_doctype != "Journal Entry":
if party_account_currency == ref_doc.company_currency: if party_account_currency == company_currency:
if ref_doc.doctype == "Expense Claim": if ref_doc.doctype == "Expense Claim":
total_amount = ref_doc.total_sanctioned_amount total_amount = ref_doc.total_sanctioned_amount
elif ref_doc.doctype == "Employee Advance":
total_amount = ref_doc.advance_amount
else: else:
total_amount = ref_doc.base_grand_total total_amount = ref_doc.base_grand_total
exchange_rate = 1 exchange_rate = 1
@ -669,15 +685,18 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
# Get the exchange rate from the original ref doc # Get the exchange rate from the original ref doc
# or get it based on the posting date of the ref doc # or get it based on the posting date of the ref doc
exchange_rate = ref_doc.get("conversion_rate") or \ exchange_rate = ref_doc.get("conversion_rate") or \
get_exchange_rate(party_account_currency, ref_doc.company_currency, ref_doc.posting_date) get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
outstanding_amount = ref_doc.get("outstanding_amount") \ if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim") \ outstanding_amount = ref_doc.get("outstanding_amount")
else flt(total_amount) - flt(ref_doc.advance_paid) elif reference_doctype == "Employee Advance":
outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount)
else:
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
else: else:
# Get the exchange rate based on the posting date of the ref doc # Get the exchange rate based on the posting date of the ref doc
exchange_rate = get_exchange_rate(party_account_currency, exchange_rate = get_exchange_rate(party_account_currency,
ref_doc.company_currency, ref_doc.posting_date) company_currency, ref_doc.posting_date)
return frappe._dict({ return frappe._dict({
"due_date": ref_doc.get("due_date"), "due_date": ref_doc.get("due_date"),
@ -686,6 +705,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
"exchange_rate": exchange_rate "exchange_rate": exchange_rate
}) })
@frappe.whitelist() @frappe.whitelist()
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
doc = frappe.get_doc(dt, dn) doc = frappe.get_doc(dt, dn)
@ -697,7 +717,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
party_type = "Customer" party_type = "Customer"
elif dt in ("Purchase Invoice", "Purchase Order"): elif dt in ("Purchase Invoice", "Purchase Order"):
party_type = "Supplier" party_type = "Supplier"
elif dt in ("Expense Claim"): elif dt in ("Expense Claim", "Employee Advance"):
party_type = "Employee" party_type = "Employee"
elif dt in ("Fees"): elif dt in ("Fees"):
party_type = "Student" party_type = "Student"
@ -709,6 +729,8 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
party_account = doc.credit_to party_account = doc.credit_to
elif dt == "Fees": elif dt == "Fees":
party_account = doc.receivable_account party_account = doc.receivable_account
elif dt == "Employee Advance":
party_account = doc.advance_account
else: else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
@ -733,7 +755,11 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
outstanding_amount = doc.outstanding_amount outstanding_amount = doc.outstanding_amount
elif dt in ("Expense Claim"): elif dt in ("Expense Claim"):
grand_total = doc.total_sanctioned_amount grand_total = doc.total_sanctioned_amount
outstanding_amount = doc.total_sanctioned_amount - doc.total_amount_reimbursed outstanding_amount = doc.total_sanctioned_amount \
- doc.total_amount_reimbursed - flt(doc.total_advance_amount)
elif dt == "Employee Advance":
grand_total = doc.advance_amount
outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount)
elif dt == "Fees": elif dt == "Fees":
grand_total = doc.grand_total grand_total = doc.grand_total
outstanding_amount = doc.outstanding_amount outstanding_amount = doc.outstanding_amount
@ -776,26 +802,16 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.received_amount = received_amount pe.received_amount = received_amount
pe.allocate_payment_amount = 1 pe.allocate_payment_amount = 1
pe.letter_head = doc.get("letter_head") pe.letter_head = doc.get("letter_head")
args = {
'party_account': party_account, 'company': pe.company, 'party_type': pe.party_type,
'party': pe.party, 'posting_date': pe.posting_date
}
references = get_outstanding_reference_documents(args=args)
for reference in references: pe.append("references", {
if reference.voucher_no == dn: 'reference_doctype': dt,
allocated_amount = min(paid_amount, reference.outstanding_amount) 'reference_name': dn,
pe.append("references", { "bill_no": doc.get("bill_no"),
'reference_doctype': reference.voucher_type, "due_date": doc.get("due_date"),
'reference_name': reference.voucher_no, 'total_amount': grand_total,
'due_date': reference.due_date, 'outstanding_amount': outstanding_amount,
'total_amount': reference.invoice_amount, 'allocated_amount': outstanding_amount
'outstanding_amount': reference.outstanding_amount, })
'allocated_amount': allocated_amount,
"bill_no": reference.get("bill_no")
})
if paid_amount:
paid_amount -= allocated_amount
pe.setup_party_account_field() pe.setup_party_account_field()
pe.set_missing_values() pe.set_missing_values()

View File

@ -66,18 +66,6 @@ class TestPaymentEntry(unittest.TestCase):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 100) self.assertEqual(outstanding_amount, 100)
def test_payment_entry_against_si_multi_due_dates(self):
si = create_sales_invoice(do_not_save=1)
si.payment_terms_template = '_Test Payment Term Template'
si.insert()
si.submit()
pe = get_payment_entry(si.doctype, si.name)
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.insert()
pe.submit()
def test_payment_entry_against_pi(self): def test_payment_entry_against_pi(self):
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC", pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
currency="USD", conversion_rate=50) currency="USD", conversion_rate=50)
@ -106,6 +94,7 @@ class TestPaymentEntry(unittest.TestCase):
pe.reference_no = "1" pe.reference_no = "1"
pe.reference_date = "2016-01-01" pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 1 pe.source_exchange_rate = 1
pe.paid_to = payable
pe.insert() pe.insert()
pe.submit() pe.submit()

View File

@ -67,7 +67,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -98,7 +98,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
@ -160,7 +160,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
@ -179,8 +179,8 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-23 12:39:02.013040", "modified": "2017-12-19 16:20:33.546984",
"modified_by": "Administrator", "modified_by": "nabinhait@gmail.com",
"module": "Accounts", "module": "Accounts",
"name": "Payment Schedule", "name": "Payment Schedule",
"name_case": "", "name_case": "",

View File

@ -106,11 +106,6 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
}; };
}; };
cur_frm.fields_dict.user.get_query = function(doc,cdt,cdn) {
return{ query:"frappe.core.doctype.user.user.user_query"};
};
cur_frm.fields_dict.write_off_account.get_query = function(doc) { cur_frm.fields_dict.write_off_account.get_query = function(doc) {
return{ return{
filters:{ filters:{

View File

@ -101,38 +101,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Applicable for User",
"length": 0,
"no_copy": 0,
"oldfieldname": "user",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 1,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -162,7 +130,7 @@
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 1, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
@ -1508,7 +1476,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-24 14:08:09.184226", "modified": "2018-01-03 17:30:45.198147",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Profile", "name": "POS Profile",

View File

@ -31,8 +31,8 @@ class POSProfile(Document):
msgprint(_("Already set default in pos profile {0} for user {1}, kindly disabled default") msgprint(_("Already set default in pos profile {0} for user {1}, kindly disabled default")
.format(res[0][0], row.user), raise_exception=1) .format(res[0][0], row.user), raise_exception=1)
elif not row.default and not res: elif not row.default and not res:
msgprint(_("Row {0}: set atleast one default pos profile for user {1}") msgprint(_("User {0} doesn't have any default POS Profile. Check Default at Row {1} for this User.")
.format(row.idx, row.user), raise_exception=1) .format(row.user, row.idx), raise_exception=1)
def validate_all_link_fields(self): def validate_all_link_fields(self):
accounts = {"Account": [self.income_account, accounts = {"Account": [self.income_account,
@ -63,7 +63,11 @@ class POSProfile(Document):
if len(default_mode_of_payment) > 1: if len(default_mode_of_payment) > 1:
frappe.throw(_("Multiple default mode of payment is not allowed")) frappe.throw(_("Multiple default mode of payment is not allowed"))
def validate_customer_territory_group(self): def validate_customer_territory_group(self):
if not frappe.db.get_single_value('POS Settings', 'use_pos_in_offline_mode'):
return
if not self.territory: if not self.territory:
frappe.throw(_("Territory is Required in POS Profile"), title="Mandatory Field") frappe.throw(_("Territory is Required in POS Profile"), title="Mandatory Field")
@ -83,12 +87,12 @@ class POSProfile(Document):
frappe.defaults.clear_default("is_pos") frappe.defaults.clear_default("is_pos")
if not include_current_pos: if not include_current_pos:
condition = " where name != '%s'" % self.name.replace("'", "\'") condition = " where pfu.name != '%s' and pfu.default = 1 " % self.name.replace("'", "\'")
else: else:
condition = "" condition = " where pfu.default = 1 "
pos_view_users = frappe.db.sql_list("""select user pos_view_users = frappe.db.sql_list("""select pfu.user
from `tabPOS Profile` {0}""".format(condition)) from `tabPOS Profile User` as pfu {0}""".format(condition))
for user in pos_view_users: for user in pos_view_users:
if user: if user:
@ -103,7 +107,7 @@ def get_item_groups(pos_profile):
if pos_profile.get('item_groups'): if pos_profile.get('item_groups'):
# Get items based on the item groups defined in the POS profile # Get items based on the item groups defined in the POS profile
for data in pos_profile.get('item_groups'): for data in pos_profile.get('item_groups'):
item_groups.extend(["'%s'"%d.name for d in get_child_nodes('Item Group', data.item_group)]) item_groups.extend(["'%s'" % frappe.db.escape(d.name) for d in get_child_nodes('Item Group', data.item_group)])
return list(set(item_groups)) return list(set(item_groups))

View File

@ -140,6 +140,37 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tax_id",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tax Id",
"length": 0,
"no_copy": 0,
"options": "supplier.tax_id",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -161,7 +192,7 @@
"oldfieldname": "due_date", "oldfieldname": "due_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@ -3086,7 +3117,7 @@
"options": "Payment Schedule", "options": "Payment Schedule",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@ -3883,7 +3914,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-11-29 13:44:40.722157", "modified": "2017-12-20 17:49:51.230092",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -277,6 +277,8 @@ class PurchaseInvoice(BuyingController):
.format(item.purchase_receipt)) .format(item.purchase_receipt))
def on_submit(self): def on_submit(self):
super(PurchaseInvoice, self).on_submit()
self.check_prev_docstatus() self.check_prev_docstatus()
self.update_status_updater_args() self.update_status_updater_args()
@ -348,7 +350,6 @@ class PurchaseInvoice(BuyingController):
self.negative_expense_to_be_booked = 0.0 self.negative_expense_to_be_booked = 0.0
gl_entries = [] gl_entries = []
self.make_supplier_gl_entry(gl_entries) self.make_supplier_gl_entry(gl_entries)
self.make_item_gl_entries(gl_entries) self.make_item_gl_entries(gl_entries)
self.make_tax_gl_entries(gl_entries) self.make_tax_gl_entries(gl_entries)
@ -363,27 +364,7 @@ class PurchaseInvoice(BuyingController):
def make_supplier_gl_entry(self, gl_entries): def make_supplier_gl_entry(self, gl_entries):
grand_total = self.rounded_total or self.grand_total grand_total = self.rounded_total or self.grand_total
if self.get("payment_schedule"): if grand_total:
for d in self.get("payment_schedule"):
payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
d.precision("payment_amount"))
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": d.due_date,
"against": self.against_expense_account,
"credit": payment_amount_in_company_currency,
"credit_in_account_currency": payment_amount_in_company_currency \
if self.party_account_currency==self.company_currency else d.payment_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype
}, self.party_account_currency)
)
elif grand_total:
# Didnot use base_grand_total to book rounding loss gle # Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate, grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total")) self.precision("grand_total"))
@ -442,7 +423,10 @@ class PurchaseInvoice(BuyingController):
# sub-contracting warehouse # sub-contracting warehouse
if flt(item.rm_supp_cost): if flt(item.rm_supp_cost):
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["name"] supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account:
frappe.throw(_("Please set account in Warehouse {0}")
.format(self.supplier_warehouse))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": supplier_warehouse_account, "account": supplier_warehouse_account,
"against": item.expense_account, "against": item.expense_account,
@ -626,6 +610,8 @@ class PurchaseInvoice(BuyingController):
)) ))
def on_cancel(self): def on_cancel(self):
super(PurchaseInvoice, self).on_cancel()
self.check_for_closed_status() self.check_for_closed_status()
self.update_status_updater_args() self.update_status_updater_args()

View File

@ -1,7 +1,7 @@
QUnit.module('Purchase Invoice'); QUnit.module('Purchase Invoice');
QUnit.test("test purchase invoice", function(assert) { QUnit.test("test purchase invoice", function(assert) {
assert.expect(6); assert.expect(9);
let done = assert.async(); let done = assert.async();
frappe.run_serially([ frappe.run_serially([
() => { () => {
@ -18,7 +18,7 @@ QUnit.test("test purchase invoice", function(assert) {
{update_stock:1}, {update_stock:1},
{supplier_address: 'Test1-Billing'}, {supplier_address: 'Test1-Billing'},
{contact_person: 'Contact 3-Test Supplier'}, {contact_person: 'Contact 3-Test Supplier'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'}, {terms: 'This is Test'},
{payment_terms_template: '_Test Payment Term Template UI'} {payment_terms_template: '_Test Payment Term Template UI'}
@ -29,7 +29,7 @@ QUnit.test("test purchase invoice", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// get tax account head details // get tax account head details
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
// grand_total Calculated // grand_total Calculated
@ -39,6 +39,33 @@ QUnit.test("test purchase invoice", function(assert) {
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
}, },
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(0.5),
() => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(0.5),
() => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(!cur_dialog, 'Message is not shown');
},
() => cur_frm.save(),
() => frappe.tests.click_button('Submit'), () => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'), () => frappe.tests.click_button('Yes'),
() => frappe.timeout(1), () => frappe.timeout(1),

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import unittest import unittest
import frappe, erpnext import frappe, erpnext
import frappe.model import frappe.model
from frappe.utils import cint, flt, today, nowdate, getdate, add_days from frappe.utils import cint, flt, today, nowdate, add_days
import frappe.defaults import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records test_records as pr_test_records
@ -27,6 +27,7 @@ class TestPurchaseInvoice(unittest.TestCase):
unlink_payment_on_cancel_of_invoice(0) unlink_payment_on_cancel_of_invoice(0)
def test_gl_entries_without_perpetual_inventory(self): def test_gl_entries_without_perpetual_inventory(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
wrapper = frappe.copy_doc(test_records[0]) wrapper = frappe.copy_doc(test_records[0])
set_perpetual_inventory(0, wrapper.company) set_perpetual_inventory(0, wrapper.company)
self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(wrapper.company))) self.assertTrue(not cint(erpnext.is_perpetual_inventory_enabled(wrapper.company)))
@ -647,39 +648,6 @@ class TestPurchaseInvoice(unittest.TestCase):
self.assertEquals(pi.total_taxes_and_charges, 462.3) self.assertEquals(pi.total_taxes_and_charges, 462.3)
self.assertEquals(pi.grand_total, 1712.3) self.assertEquals(pi.grand_total, 1712.3)
def test_gl_entry_based_on_payment_schedule(self):
pi = make_purchase_invoice(do_not_save=True, supplier="_Test Supplier P")
pi.append("payment_schedule", {
"due_date": add_days(nowdate(), 15),
"payment_amount": 100,
"invoice_portion": 40.00
})
pi.append("payment_schedule", {
"due_date": add_days(nowdate(), 25),
"payment_amount": 150,
"invoice_portion": 60.00
})
pi.save()
pi.submit()
gl_entries = frappe.db.sql("""select account, debit, credit, due_date
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc, debit asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
expected_gl_entries = sorted([
[pi.credit_to, 0.0, 100.0, add_days(nowdate(), 15)],
[pi.credit_to, 0.0, 150.0, add_days(nowdate(), 25)],
["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, None]
])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
self.assertEquals(expected_gl_entries[i][0], gle.account)
self.assertEquals(expected_gl_entries[i][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit)
self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
def test_make_pi_without_terms(self): def test_make_pi_without_terms(self):
pi = make_purchase_invoice(do_not_save=1) pi = make_purchase_invoice(do_not_save=1)

View File

@ -3,6 +3,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \ from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \
import valdiate_taxes_and_charges_template import valdiate_taxes_and_charges_template
@ -10,3 +11,8 @@ from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_c
class PurchaseTaxesandChargesTemplate(Document): class PurchaseTaxesandChargesTemplate(Document):
def validate(self): def validate(self):
valdiate_taxes_and_charges_template(self) valdiate_taxes_and_charges_template(self)
def autoname(self):
if self.company and self.title:
abbr = frappe.db.get_value('Company', self.company, 'abbr')
self.name = '{0} - {1}'.format(self.title, abbr)

View File

@ -1,7 +1,7 @@
QUnit.module('Sales Taxes and Charges Template'); QUnit.module('Sales Taxes and Charges Template');
QUnit.test("test sales taxes and charges template", function(assert) { QUnit.test("test sales taxes and charges template", function(assert) {
assert.expect(1); assert.expect(2);
let done = assert.async(); let done = assert.async();
frappe.run_serially([ frappe.run_serially([
() => { () => {
@ -19,7 +19,10 @@ QUnit.test("test sales taxes and charges template", function(assert) {
]} ]}
]); ]);
}, },
() => {assert.ok(cur_frm.doc.title=='TEST In State GST');}, () => {
assert.ok(cur_frm.doc.title=='TEST In State GST');
assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
},
() => done() () => done()
]); ]);
}); });

View File

@ -503,7 +503,7 @@
"oldfieldname": "due_date", "oldfieldname": "due_date",
"oldfieldtype": "Date", "oldfieldtype": "Date",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@ -2864,7 +2864,7 @@
"options": "Payment Schedule", "options": "Payment Schedule",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@ -4563,7 +4563,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-11-29 17:36:05.216046", "modified": "2017-12-20 17:36:05.216046",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -143,6 +143,7 @@ class SalesInvoice(SellingController):
self.update_time_sheet(self.name) self.update_time_sheet(self.name)
self.update_current_month_sales() self.update_current_month_sales()
self.update_project()
def validate_pos_paid_amount(self): def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos: if len(self.payments) == 0 and self.is_pos:
@ -181,6 +182,7 @@ class SalesInvoice(SellingController):
frappe.db.set(self, 'status', 'Cancelled') frappe.db.set(self, 'status', 'Cancelled')
self.update_current_month_sales() self.update_current_month_sales()
self.update_project()
def update_current_month_sales(self): def update_current_month_sales(self):
if frappe.flags.in_test: if frappe.flags.in_test:
@ -635,27 +637,7 @@ class SalesInvoice(SellingController):
def make_customer_gl_entry(self, gl_entries): def make_customer_gl_entry(self, gl_entries):
grand_total = self.rounded_total or self.grand_total grand_total = self.rounded_total or self.grand_total
if self.get("payment_schedule"): if grand_total:
for d in self.get("payment_schedule"):
payment_amount_in_company_currency = flt(d.payment_amount * self.conversion_rate,
d.precision("payment_amount"))
gl_entries.append(
self.get_gl_dict({
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": d.due_date,
"against": self.against_income_account,
"debit": payment_amount_in_company_currency,
"debit_in_account_currency": payment_amount_in_company_currency \
if self.party_account_currency==self.company_currency else d.payment_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype
}, self.party_account_currency)
)
elif grand_total:
# Didnot use base_grand_total to book rounding loss gle # Didnot use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate, grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total")) self.precision("grand_total"))
@ -912,6 +894,13 @@ class SalesInvoice(SellingController):
serial_no, sales_invoice serial_no, sales_invoice
))) )))
def update_project(self):
if self.project:
project = frappe.get_doc("Project", self.project)
project.flags.dont_sync_tasks = True
project.update_billed_amount()
project.save()
def get_list_context(context=None): def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context) list_context = get_list_context(context)
@ -991,4 +980,4 @@ def make_sales_return(source_name, target_doc=None):
def set_account_for_mode_of_payment(self): def set_account_for_mode_of_payment(self):
for data in self.payments: for data in self.payments:
if not data.account: if not data.account:
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account") data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")

View File

@ -1,7 +1,7 @@
QUnit.module('Sales Invoice'); QUnit.module('Sales Invoice');
QUnit.test("test sales Invoice", function(assert) { QUnit.test("test sales Invoice", function(assert) {
assert.expect(6); assert.expect(9);
let done = assert.async(); let done = assert.async();
frappe.run_serially([ frappe.run_serially([
() => { () => {
@ -17,7 +17,7 @@ QUnit.test("test sales Invoice", function(assert) {
{customer_address: 'Test1-Billing'}, {customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'}, {shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'}, {contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'}, {terms: 'This is Test'},
{payment_terms_template: '_Test Payment Term Template UI'} {payment_terms_template: '_Test Payment Term Template UI'}
@ -28,7 +28,7 @@ QUnit.test("test sales Invoice", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// get tax account head details // get tax account head details
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
// grand_total Calculated // grand_total Calculated
@ -38,6 +38,33 @@ QUnit.test("test sales Invoice", function(assert) {
assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty"); assert.ok(cur_frm.doc.payment_schedule.length > 0, "Payment Term Schedule is not empty");
}, },
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(0.5),
() => frappe.tests.set_form_values(cur_frm, [{'payment_terms_schedule': ''}]),
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(cur_dialog && cur_dialog.is_visible, 'Message is displayed to user');
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(0.5),
() => frappe.tests.set_form_values(cur_frm, [{'payment_schedule': []}]),
() => {
let date = cur_frm.doc.due_date;
frappe.tests.set_control('due_date', frappe.datetime.add_days(date, 1));
frappe.timeout(0.5);
assert.ok(!cur_dialog, 'Message is not shown');
},
() => cur_frm.save(),
() => frappe.tests.click_button('Submit'), () => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'), () => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.3), () => frappe.timeout(0.3),

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest, copy, time import unittest, copy, time
from frappe.utils import nowdate, add_days, flt, getdate, cint from frappe.utils import nowdate, flt, getdate, cint
from frappe.model.dynamic_links import get_dynamic_link_map from frappe.model.dynamic_links import get_dynamic_link_map
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
@ -1321,40 +1321,6 @@ class TestSalesInvoice(unittest.TestCase):
}) })
si.insert() si.insert()
return si return si
def test_gl_entry_based_on_payment_schedule(self):
si = create_sales_invoice(do_not_save=True, customer="_Test Customer P")
si.append("payment_schedule", {
"due_date": add_days(nowdate(), 15),
"payment_amount": 20,
"invoice_portion": 20.00
})
si.append("payment_schedule", {
"due_date": add_days(nowdate(), 45),
"payment_amount": 80,
"invoice_portion": 80.00
})
si.save()
si.submit()
gl_entries = frappe.db.sql("""select account, debit, credit, due_date
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc, debit asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
expected_gl_entries = sorted([
[si.debit_to, 20.0, 0.0, add_days(nowdate(), 15)],
[si.debit_to, 80.0, 0.0, add_days(nowdate(), 45)],
["Sales - _TC", 0.0, 100.0, None]
])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
self.assertEquals(expected_gl_entries[i][0], gle.account)
self.assertEquals(expected_gl_entries[i][1], gle.debit)
self.assertEquals(expected_gl_entries[i][2], gle.credit)
self.assertEquals(getdate(expected_gl_entries[i][3]), getdate(gle.due_date))
def test_company_monthly_sales(self): def test_company_monthly_sales(self):
existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales") existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")

View File

@ -17,7 +17,7 @@ QUnit.test("test sales Invoice", function(assert) {
{customer_address: 'Test1-Billing'}, {customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'}, {shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'}, {contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'} {terms: 'This is Test'}
]); ]);
@ -27,7 +27,7 @@ QUnit.test("test sales Invoice", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// get tax account head details // get tax account head details
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
// grand_total Calculated // grand_total Calculated

View File

@ -17,7 +17,7 @@ QUnit.test("test sales Invoice with payment", function(assert) {
{customer_address: 'Test1-Billing'}, {customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'}, {shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'}, {contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'}, {terms: 'This is Test'},
{payment_terms_template: '_Test Payment Term Template UI'} {payment_terms_template: '_Test Payment Term Template UI'}
@ -28,7 +28,7 @@ QUnit.test("test sales Invoice with payment", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// grand_total Calculated // grand_total Calculated
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");

View File

@ -17,7 +17,7 @@ QUnit.test("test sales Invoice with payment request", function(assert) {
{customer_address: 'Test1-Billing'}, {customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'}, {shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'}, {contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'} {terms: 'This is Test'}
]); ]);
@ -27,7 +27,7 @@ QUnit.test("test sales Invoice with payment request", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// grand_total Calculated // grand_total Calculated
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct"); assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");

View File

@ -17,7 +17,7 @@ QUnit.test("test sales Invoice with serialize item", function(assert) {
{customer_address: 'Test1-Billing'}, {customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'}, {shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'}, {contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'}, {taxes_and_charges: 'TEST In State GST - FT'},
{tc_name: 'Test Term 1'}, {tc_name: 'Test Term 1'},
{terms: 'This is Test'} {terms: 'This is Test'}
]); ]);
@ -27,7 +27,7 @@ QUnit.test("test sales Invoice with serialize item", function(assert) {
// get_item_details // get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct"); assert.ok(cur_frm.doc.items[0].item_name=='Test Product 4', "Item name correct");
// get tax details // get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct"); assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST - FT', "Tax details correct");
// get tax account head details // get tax account head details
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct"); assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
// get batch number // get batch number

View File

@ -11,7 +11,12 @@ from erpnext.controllers.accounts_controller import validate_taxes_and_charges,
class SalesTaxesandChargesTemplate(Document): class SalesTaxesandChargesTemplate(Document):
def validate(self): def validate(self):
valdiate_taxes_and_charges_template(self) valdiate_taxes_and_charges_template(self)
def autoname(self):
if self.company and self.title:
abbr = frappe.db.get_value('Company', self.company, 'abbr')
self.name = '{0} - {1}'.format(self.title, abbr)
def set_missing_values(self): def set_missing_values(self):
for data in self.taxes: for data in self.taxes:
if data.charge_type == 'On Net Total' and flt(data.rate) == 0.0: if data.charge_type == 'On Net Total' and flt(data.rate) == 0.0:

View File

@ -1,7 +1,7 @@
QUnit.module('Sales Taxes and Charges Template'); QUnit.module('Sales Taxes and Charges Template');
QUnit.test("test sales taxes and charges template", function(assert) { QUnit.test("test sales taxes and charges template", function(assert) {
assert.expect(1); assert.expect(2);
let done = assert.async(); let done = assert.async();
frappe.run_serially([ frappe.run_serially([
() => { () => {
@ -19,7 +19,10 @@ QUnit.test("test sales taxes and charges template", function(assert) {
]} ]}
]); ]);
}, },
() => {assert.ok(cur_frm.doc.title=='TEST In State GST');}, () => {
assert.ok(cur_frm.doc.title=='TEST In State GST');
assert.ok(cur_frm.doc.name=='TEST In State GST - FT');
},
() => done() () => done()
]); ]);
}); });

View File

@ -2,7 +2,7 @@
{ {
"doctype": "Tax Rule", "doctype": "Tax Rule",
"tax_type" : "Sales", "tax_type" : "Sales",
"sales_tax_template": "_Test Tax 1", "sales_tax_template": "_Test Tax 1 - _TC",
"use_for_shopping_cart": 1, "use_for_shopping_cart": 1,
"billing_city": "_Test City", "billing_city": "_Test City",
"billing_state": "Test State", "billing_state": "Test State",
@ -15,7 +15,7 @@
{ {
"doctype": "Tax Rule", "doctype": "Tax Rule",
"tax_type" : "Sales", "tax_type" : "Sales",
"sales_tax_template": "_Test Tax 2", "sales_tax_template": "_Test Tax 2 - _TC",
"use_for_shopping_cart": 0, "use_for_shopping_cart": 0,
"billing_city": "_Test City", "billing_city": "_Test City",
"billing_country": "India", "billing_country": "India",

View File

@ -18,40 +18,40 @@ class TestTaxRule(unittest.TestCase):
def test_conflict(self): def test_conflict(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer", tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1)
tax_rule1.save() tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer", tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1)
self.assertRaises(ConflictingTaxRule, tax_rule2.save) self.assertRaises(ConflictingTaxRule, tax_rule2.save)
def test_conflict_with_non_overlapping_dates(self): def test_conflict_with_non_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer", tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01") sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01")
tax_rule1.save() tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer", tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, to_date = "2013-01-01") sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, to_date = "2013-01-01")
tax_rule2.save() tax_rule2.save()
self.assertTrue(tax_rule2.name) self.assertTrue(tax_rule2.name)
def test_for_parent_customer_group(self): def test_for_parent_customer_group(self):
tax_rule1 = make_tax_rule(customer_group= "All Customer Groups", tax_rule1 = make_tax_rule(customer_group= "All Customer Groups",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01") sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01")
tax_rule1.save() tax_rule1.save()
self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":0}), self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial", "use_for_shopping_cart":0}),
"_Test Sales Taxes and Charges Template") "_Test Sales Taxes and Charges Template - _TC")
def test_conflict_with_overlapping_dates(self): def test_conflict_with_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer", tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05") sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")
tax_rule1.save() tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer", tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-03", to_date = "2015-01-09") sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority = 1, from_date = "2015-01-03", to_date = "2015-01-09")
self.assertRaises(ConflictingTaxRule, tax_rule2.save) self.assertRaises(ConflictingTaxRule, tax_rule2.save)
@ -62,66 +62,66 @@ class TestTaxRule(unittest.TestCase):
def test_select_tax_rule_based_on_customer(self): def test_select_tax_rule_based_on_customer(self):
make_tax_rule(customer= "_Test Customer", make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
make_tax_rule(customer= "_Test Customer 1", make_tax_rule(customer= "_Test Customer 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
make_tax_rule(customer= "_Test Customer 2", make_tax_rule(customer= "_Test Customer 2",
sales_tax_template = "_Test Sales Taxes and Charges Template 2", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 2 - _TC", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer 2"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer 2"}),
"_Test Sales Taxes and Charges Template 2") "_Test Sales Taxes and Charges Template 2 - _TC")
def test_select_tax_rule_based_on_better_match(self): def test_select_tax_rule_based_on_better_match(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", billing_state = "Test State", make_tax_rule(customer= "_Test Customer", billing_city = "Test City", billing_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
make_tax_rule(customer= "_Test Customer", billing_city = "Test City1", billing_state = "Test State", make_tax_rule(customer= "_Test Customer", billing_city = "Test City1", billing_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City", "billing_state": "Test State"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City", "billing_state": "Test State"}),
"_Test Sales Taxes and Charges Template") "_Test Sales Taxes and Charges Template - _TC")
def test_select_tax_rule_based_on_state_match(self): def test_select_tax_rule_based_on_state_match(self):
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State", make_tax_rule(customer= "_Test Customer", shipping_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State12", make_tax_rule(customer= "_Test Customer", shipping_state = "Test State12",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", priority=2, save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "shipping_state": "Test State"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "shipping_state": "Test State"}),
"_Test Sales Taxes and Charges Template") "_Test Sales Taxes and Charges Template - _TC")
def test_select_tax_rule_based_on_better_priority(self): def test_select_tax_rule_based_on_better_priority(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority=1, save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", priority=1, save=1)
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", priority=2, save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City"}),
"_Test Sales Taxes and Charges Template 1") "_Test Sales Taxes and Charges Template 1 - _TC")
def test_select_tax_rule_based_cross_matching_keys(self): def test_select_tax_rule_based_cross_matching_keys(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
make_tax_rule(customer= "_Test Customer 1", billing_city = "Test City 1", make_tax_rule(customer= "_Test Customer 1", billing_city = "Test City 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
None) None)
def test_select_tax_rule_based_cross_partially_keys(self): def test_select_tax_rule_based_cross_partially_keys(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template - _TC", save=1)
make_tax_rule(billing_city = "Test City 1", make_tax_rule(billing_city = "Test City 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1) sales_tax_template = "_Test Sales Taxes and Charges Template 1 - _TC", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}), self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
"_Test Sales Taxes and Charges Template 1") "_Test Sales Taxes and Charges Template 1 - _TC")
def make_tax_rule(**args): def make_tax_rule(**args):

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, erpnext import frappe, erpnext
from frappe.utils import flt, cstr, cint, getdate from frappe.utils import flt, cstr, cint
from frappe import _ from frappe import _
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
@ -75,8 +75,7 @@ def check_if_in_list(gle, gl_map):
and cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \ and cstr(e.get('against_voucher'))==cstr(gle.get('against_voucher')) \
and cstr(e.get('against_voucher_type')) == cstr(gle.get('against_voucher_type')) \ and cstr(e.get('against_voucher_type')) == cstr(gle.get('against_voucher_type')) \
and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')) \ and cstr(e.get('cost_center')) == cstr(gle.get('cost_center')) \
and cstr(e.get('project')) == cstr(gle.get('project')) \ and cstr(e.get('project')) == cstr(gle.get('project')):
and getdate(e.get('due_date')) == getdate(gle.get('due_date')):
return e return e
def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):

View File

@ -2,15 +2,15 @@
"align_labels_right": 0, "align_labels_right": 0,
"creation": "2017-08-08 12:33:04.773099", "creation": "2017-08-08 12:33:04.773099",
"custom_format": 1, "custom_format": 1,
"disabled": 0, "disabled": 1,
"doc_type": "Sales Invoice", "doc_type": "Sales Invoice",
"docstatus": 0, "docstatus": 0,
"doctype": "Print Format", "doctype": "Print Format",
"font": "Default", "font": "Default",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n<p><b>Tax Breakup:</b></p>\n<div style=\"font-size: 8px\">\n\t{{ doc.other_charges_calculation }}\n</div>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>", "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t<b>{{ _(\"GSTIN\") }}:</b>{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"<br>GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t<br>\n\t<b>{{ doc.select_print_heading or _(\"Invoice\") }}</b><br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"<br>\", \" \") %}\n\t\t<b>{{ _(\"Customer\") }}:</b><br>\n\t\t{{ doc.customer_name }}<br>\n\t\t{{ customer_address }}\n\t{% endif %}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"40%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"30%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t<br><b>{{ _(\"HSN/SAC\") }}:</b> {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t<br><b>{{ _(\"Serial No\") }}:</b> {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.rate }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n<p><b>Tax Breakup:</b></p>\n<div style=\"font-size: 8px\">\n\t{{ doc.other_charges_calculation }}\n</div>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
"idx": 0, "idx": 0,
"line_breaks": 0, "line_breaks": 0,
"modified": "2017-09-14 15:54:19.467642", "modified": "2018-01-05 17:25:59.181985",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "GST POS Invoice", "name": "GST POS Invoice",

View File

@ -1,15 +1,21 @@
{ {
"align_labels_right": 0,
"creation": "2011-12-21 11:08:55", "creation": "2011-12-21 11:08:55",
"custom_format": 1, "custom_format": 1,
"disabled": 0,
"doc_type": "Sales Invoice", "doc_type": "Sales Invoice",
"docstatus": 0, "docstatus": 0,
"doctype": "Print Format", "doctype": "Print Format",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ _(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"net_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t{%- if not row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n{% if doc.get(\"taxes\", filters={\"included_in_print_rate\": 1}) %}\n<hr>\n<p><b>Taxes Included:</b></p>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t{%- for row in doc.taxes -%}\n\t\t{%- if row.included_in_print_rate -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ row.get_formatted(\"tax_amount_after_discount_amount\", doc) }}\n\t\t\t</td>\n\t\t<tr>\n\t\t{%- endif -%}\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n{%- endif -%}\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>", "html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Invoice\") }}<br>\n</p>\n<p>\n\t<b>{{ _(\"Receipt No\") }}:</b> {{ doc.name }}<br>\n\t<b>{{ _(\"Date\") }}:</b> {{ doc.get_formatted(\"posting_date\") }}<br>\n\t<b>{{ _(\"Customer\") }}:</b> {{ doc.customer_name }}\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ _(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ _(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{%- for item in doc.items -%}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t<br>{{ item.item_name }}{%- endif -%}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ item.qty }}<br>@ {{ item.get_formatted(\"rate\") }}</td>\n\t\t\t<td class=\"text-right\">{{ item.get_formatted(\"amount\") }}</td>\n\t\t</tr>\n\t\t{%- endfor -%}\n\t</tbody>\n</table>\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% else %}\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t</td>\n\t\t\t{% endif %}\n\t\t</tr>\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t<tr>\n\t\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t</td>\n\t\t\t\t<td class=\"text-right\">\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t</td>\n\t\t\t<tr>\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{%- endif -%}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ _(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n<hr>\n<p>{{ doc.terms or \"\" }}</p>\n<p class=\"text-center\">{{ _(\"Thank you, please visit again.\") }}</p>",
"idx": 1, "idx": 1,
"modified": "2015-04-21 05:06:29.380856", "line_breaks": 0,
"modified": "2018-01-05 17:23:40.403289",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice", "name": "POS Invoice",
"owner": "Administrator", "owner": "Administrator",
"print_format_builder": 0,
"print_format_type": "Server", "print_format_type": "Server",
"show_section_headings": 0,
"standard": "Yes" "standard": "Yes"
} }

View File

@ -113,7 +113,7 @@ class ReceivablePayableReport(object):
row += [self.get_party_name(gle.party_type, gle.party)] row += [self.get_party_name(gle.party_type, gle.party)]
# get due date # get due date
due_date = gle.due_date or voucher_details.get(gle.voucher_no, {}).get("due_date", "") due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
row += [gle.voucher_type, gle.voucher_no, due_date] row += [gle.voucher_type, gle.voucher_no, due_date]
@ -188,8 +188,7 @@ class ReceivablePayableReport(object):
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date and e.name!=gle.name \ if getdate(e.posting_date) <= report_date and e.name!=gle.name:
and (not gle.due_date or getdate(e.due_date) == getdate(gle.due_date)):
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr)) amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
if e.voucher_no not in return_entries: if e.voucher_no not in return_entries:
payment_amount += amount payment_amount += amount
@ -251,11 +250,11 @@ class ReceivablePayableReport(object):
select_fields = "sum(debit) as debit, sum(credit) as credit" select_fields = "sum(debit) as debit, sum(credit) as credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party, self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher, due_date, voucher_type, voucher_no, against_voucher_type, against_voucher,
account_currency, remarks, {0} account_currency, remarks, {0}
from `tabGL Entry` from `tabGL Entry`
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1} where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
group by voucher_type, voucher_no, against_voucher_type, against_voucher, party, due_date group by voucher_type, voucher_no, against_voucher_type, against_voucher, party
order by posting_date, party""" order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True) .format(select_fields, conditions), values, as_dict=True)

View File

@ -193,6 +193,24 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
# closing # closing
data.append(totals.closing) data.append(totals.closing)
#total closing
total_closing = totals.total_closing
total_debit = totals.closing.get('debit', 0)
total_credit = totals.closing.get('credit', 0)
debit_in_account_currency = totals.closing.get('debit_in_account_currency', 0)
credit_in_account_currency = totals.closing.get('credit_in_account_currency', 0)
total_amount = total_debit - total_credit
if total_amount > 0:
total_closing['debit'] = total_amount
total_closing['debit_in_account_currency'] = debit_in_account_currency - credit_in_account_currency
else:
total_closing['credit'] = abs(total_amount)
total_closing['credit_in_account_currency'] = abs(debit_in_account_currency - credit_in_account_currency)
data.append(totals.total_closing)
return data return data
def get_totals_dict(): def get_totals_dict():
@ -207,7 +225,8 @@ def get_totals_dict():
return _dict( return _dict(
opening = _get_debit_credit_dict(_('Opening')), opening = _get_debit_credit_dict(_('Opening')),
total = _get_debit_credit_dict(_('Total')), total = _get_debit_credit_dict(_('Total')),
closing = _get_debit_credit_dict(_('Closing (Opening + Total)')) closing = _get_debit_credit_dict(_('Closing (Opening + Total)')),
total_closing = _get_debit_credit_dict(_('Closing Balance (Dr - Cr)'))
) )
def initialize_gle_map(gl_entries): def initialize_gle_map(gl_entries):
@ -233,6 +252,9 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes": if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
update_value_in_dict(gle_map[gle.account].totals, 'opening', gle) update_value_in_dict(gle_map[gle.account].totals, 'opening', gle)
update_value_in_dict(totals, 'opening', gle) update_value_in_dict(totals, 'opening', gle)
update_value_in_dict(gle_map[gle.account].totals, 'closing', gle)
update_value_in_dict(totals, 'closing', gle)
elif gle.posting_date <= to_date: elif gle.posting_date <= to_date:
update_value_in_dict(gle_map[gle.account].totals, 'total', gle) update_value_in_dict(gle_map[gle.account].totals, 'total', gle)
@ -242,8 +264,8 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
else: else:
entries.append(gle) entries.append(gle)
update_value_in_dict(gle_map[gle.account].totals, 'closing', gle) update_value_in_dict(gle_map[gle.account].totals, 'closing', gle)
update_value_in_dict(totals, 'closing', gle) update_value_in_dict(totals, 'closing', gle)
return totals, entries return totals, entries

View File

@ -1,7 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales Register"] = { frappe.query_reports["Item-wise Sales Register"] = {
"filters": [ "filters": [
{ {
"fieldname":"from_date", "fieldname":"from_date",

View File

@ -6,6 +6,7 @@ import frappe, erpnext
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from frappe.model.meta import get_field_precision from frappe.model.meta import get_field_precision
from frappe.utils.xlsxutils import handle_html
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
def execute(filters=None): def execute(filters=None):
@ -188,10 +189,10 @@ def get_tax_accounts(item_list, columns, company_currency,
tuple([doctype] + invoice_item_row.keys())) tuple([doctype] + invoice_item_row.keys()))
for parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details: for parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
description = handle_html(description)
if description not in tax_columns and tax_amount: if description not in tax_columns and tax_amount:
# as description is text editor earlier and markup can break the column convention in reports # as description is text editor earlier and markup can break the column convention in reports
from frappe.utils.xlsxutils import handle_html tax_columns.append(description)
tax_columns.append(handle_html(description))
if item_wise_tax_detail: if item_wise_tax_detail:
try: try:

View File

@ -15,12 +15,12 @@ def execute(filters=None):
def get_column(): def get_column():
return [ return [
_("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Date") + ":Date:100", _("Purchase Receipt") + ":Link/Purchase Receipt:120", _("Date") + ":Date:100",
_("Suplier") + ":Link/Supplier:120", _("Suplier Name") + "::120", _("Supplier") + ":Link/Supplier:120", _("Supplier Name") + "::120",
_("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120", _("Project") + ":Link/Project:120", _("Item Code") + ":Link/Item:120",
_("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100", _("Amount") + ":Currency:100", _("Billed Amount") + ":Currency:100", _("Amount to Bill") + ":Currency:100",
_("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120", _("Item Name") + "::120", _("Description") + "::120", _("Company") + ":Link/Company:120",
] ]
def get_args(): def get_args():
return {'doctype': 'Purchase Receipt', 'party': 'supplier', return {'doctype': 'Purchase Receipt', 'party': 'supplier',
'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'}

View File

@ -12,7 +12,7 @@
"module": "Accounts", "module": "Accounts",
"name": "Sales Partners Commission", "name": "Sales Partners Commission",
"owner": "Administrator", "owner": "Administrator",
"query": "SELECT\n sales_partner as \"Sales Partner:Link/Sales Partner:150\",\n\tsum(base_net_total) as \"Invoiced Amount (Exculsive Tax):Currency:210\",\n\tsum(total_commission) as \"Total Commission:Currency:150\",\n\tsum(total_commission)*100/sum(base_net_total) as \"Average Commission Rate:Currency:170\"\nFROM\n\t`tabSales Invoice`\nWHERE\n\tdocstatus = 1 and ifnull(base_net_total, 0) > 0 and ifnull(total_commission, 0) > 0\nGROUP BY\n\tsales_partner\nORDER BY\n\t\"Total Commission:Currency:120\"", "query": "SELECT\n sales_partner as \"Sales Partner:Link/Sales Partner:150\",\n\tsum(base_net_total) as \"Invoiced Amount (Exclusive Tax):Currency:210\",\n\tsum(total_commission) as \"Total Commission:Currency:150\",\n\tsum(total_commission)*100/sum(base_net_total) as \"Average Commission Rate:Currency:170\"\nFROM\n\t`tabSales Invoice`\nWHERE\n\tdocstatus = 1 and ifnull(base_net_total, 0) > 0 and ifnull(total_commission, 0) > 0\nGROUP BY\n\tsales_partner\nORDER BY\n\t\"Total Commission:Currency:120\"",
"ref_doctype": "Sales Invoice", "ref_doctype": "Sales Invoice",
"report_name": "Sales Partners Commission", "report_name": "Sales Partners Commission",
"report_type": "Query Report", "report_type": "Query Report",
@ -24,4 +24,4 @@
"role": "Accounts User" "role": "Accounts User"
} }
] ]
} }

View File

@ -1,6 +1,5 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.query_reports["Sales Payment Summary"] = { frappe.query_reports["Sales Payment Summary"] = {
"filters": [ "filters": [
{ {
@ -8,12 +7,14 @@ frappe.query_reports["Sales Payment Summary"] = {
"label": __("From Date"), "label": __("From Date"),
"fieldtype": "Date", "fieldtype": "Date",
"default": frappe.datetime.get_today(), "default": frappe.datetime.get_today(),
"reqd": 1,
"width": "80" "width": "80"
}, },
{ {
"fieldname":"to_date", "fieldname":"to_date",
"label": __("To Date"), "label": __("To Date"),
"fieldtype": "Date", "fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.get_today() "default": frappe.datetime.get_today()
}, },
{ {
@ -23,12 +24,6 @@ frappe.query_reports["Sales Payment Summary"] = {
"options": "Company", "options": "Company",
"default": frappe.defaults.get_user_default("Company") "default": frappe.defaults.get_user_default("Company")
}, },
{
"fieldname":"mode_of_payment",
"label": __("Mode of Payment"),
"fieldtype": "Link",
"options": "Mode of Payment"
},
{ {
"fieldname":"owner", "fieldname":"owner",
"label": __("Owner"), "label": __("Owner"),
@ -36,22 +31,15 @@ frappe.query_reports["Sales Payment Summary"] = {
"options": "User", "options": "User",
"defaults": user "defaults": user
}, },
{
"fieldname":"cost_center",
"label": __("Cost Center"),
"fieldtype": "Link",
"options": "Cost Center"
},
{
"fieldname":"warehouse",
"label": __("Warehouse"),
"fieldtype": "Link",
"options": "Warehouse"
},
{ {
"fieldname":"is_pos", "fieldname":"is_pos",
"label": __("POS?"), "label": __("Show only POS"),
"fieldtype": "Check" "fieldtype": "Check"
} },
{
"fieldname":"payment_detail",
"label": __("Show Payment Details"),
"fieldtype": "Check"
},
] ]
}; };

View File

@ -1,9 +1,9 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import cstr
def execute(filters=None): def execute(filters=None):
columns, data = [], [] columns, data = [], []
@ -14,53 +14,133 @@ def execute(filters=None):
def get_columns(): def get_columns():
return [ return [
_("Date") + ":Date:80", _("Date") + ":Date:80",
_("Owner") + "::150", _("Owner") + ":Data:200",
_("Payment Mode") + "::120", _("Payment Mode") + ":Data:240",
_("Warehouse") + ":Link/Cost Center:100",
_("Cost Center") + ":Link/Warehouse:100",
_("Sales and Returns") + ":Currency/currency:120", _("Sales and Returns") + ":Currency/currency:120",
_("Taxes") + ":Currency/currency:120", _("Taxes") + ":Currency/currency:120",
_("Payments") + ":Currency/currency:120", _("Payments") + ":Currency/currency:120"
_("Reconciliation") + ":Currency/currency:120"
] ]
def get_sales_payment_data(filters, columns): def get_sales_payment_data(filters, columns):
sales_invoice_data = get_sales_invoice_data(filters)
data = [] data = []
show_payment_detail = False
sales_invoice_data = get_sales_invoice_data(filters)
mode_of_payments = get_mode_of_payments(filters)
mode_of_payment_details = get_mode_of_payment_details(filters)
if filters.get("payment_detail"):
show_payment_detail = True
else:
show_payment_detail = False
for inv in sales_invoice_data: for inv in sales_invoice_data:
row = [inv.posting_date, inv.owner, inv.mode_of_payment,inv.warehouse, owner_posting_date = inv["owner"]+cstr(inv["posting_date"])
inv.cost_center,inv.net_total, inv.total_taxes, inv.paid_amount, if show_payment_detail:
(inv.net_total + inv.total_taxes - inv.paid_amount)] row = [inv.posting_date, inv.owner," ",inv.net_total,inv.total_taxes, 0]
data.append(row) data.append(row)
for mop_detail in mode_of_payment_details.get(owner_posting_date,[]):
row = [inv.posting_date, inv.owner,mop_detail[0],0,0,mop_detail[1],0]
data.append(row)
else:
total_payment = 0
for mop_detail in mode_of_payment_details.get(owner_posting_date,[]):
total_payment = total_payment + mop_detail[1]
row = [inv.posting_date, inv.owner,", ".join(mode_of_payments.get(owner_posting_date, [])),
inv.net_total,inv.total_taxes,total_payment]
data.append(row)
return data return data
def get_conditions(filters): def get_conditions(filters):
conditions = "" conditions = ""
if filters.get("company"): conditions += " a.company=%(company)s" if filters.get("from_date"): conditions += "a.posting_date >= %(from_date)s"
if filters.get("to_date"): conditions += " and a.posting_date <= %(to_date)s"
if filters.get("company"): conditions += " and a.company=%(company)s"
if filters.get("customer"): conditions += " and a.customer = %(customer)s" if filters.get("customer"): conditions += " and a.customer = %(customer)s"
if filters.get("owner"): conditions += " and a.owner = %(owner)s" if filters.get("owner"): conditions += " and a.owner = %(owner)s"
if filters.get("from_date"): conditions += " and a.posting_date >= %(from_date)s"
if filters.get("to_date"): conditions += " and a.posting_date <= %(to_date)s"
if filters.get("mode_of_payment"): conditions += " and c.mode_of_payment >= %(mode_of_payment)s"
if filters.get("warehouse"): conditions += " and b.warehouse <= %(warehouse)s"
if filters.get("cost_center"): conditions += " and b.cost_center <= %(cost_center)s"
if filters.get("is_pos"): conditions += " and a.is_pos = %(is_pos)s" if filters.get("is_pos"): conditions += " and a.is_pos = %(is_pos)s"
return conditions return conditions
def get_sales_invoice_data(filters): def get_sales_invoice_data(filters):
conditions = get_conditions(filters) conditions = get_conditions(filters)
return frappe.db.sql(""" return frappe.db.sql("""
select select
a.owner, a.posting_date, c.mode_of_payment, b.warehouse, b.cost_center, a.posting_date, a.owner,
sum(a.net_total) as "net_total", sum(a.net_total) as "net_total",
sum(a.total_taxes_and_charges) as "total_taxes", sum(a.total_taxes_and_charges) as "total_taxes",
sum(a.base_paid_amount) as "paid_amount" sum(a.base_paid_amount) as "paid_amount",
from `tabSales Invoice` a, `tabSales Invoice Item` b, `tabSales Invoice Payment` c sum(a.outstanding_amount) as "outstanding_amount"
where from `tabSales Invoice` a
a.name = b.parent where a.docstatus = 1
and a.name = c.parent
and {conditions} and {conditions}
group by group by
a.owner, a.posting_date, c.mode_of_payment, b.warehouse, b.cost_center a.owner, a.posting_date
""".format(conditions=conditions), filters, as_dict=1) """.format(conditions=conditions), filters, as_dict=1)
def get_mode_of_payments(filters):
mode_of_payments = {}
invoice_list = get_invoices(filters)
invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
if invoice_list:
inv_mop = frappe.db.sql("""select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
from `tabSales Invoice` a, `tabSales Invoice Payment` b
where a.name = b.parent
and a.name in ({invoice_list_names})
union
select a.owner,a.posting_date, ifnull(b.mode_of_payment, '') as mode_of_payment
from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
where a.name = c.reference_name
and b.name = c.parent
and a.name in ({invoice_list_names})
union
select a.owner, a.posting_date,
ifnull(a.voucher_type,'') as mode_of_payment
from `tabJournal Entry` a, `tabJournal Entry Account` b
where a.name = b.parent
and a.docstatus = 1
and b.reference_type = "Sales Invoice"
and b.reference_name in ({invoice_list_names})
""".format(invoice_list_names=invoice_list_names), as_dict=1)
for d in inv_mop:
mode_of_payments.setdefault(d["owner"]+cstr(d["posting_date"]), []).append(d.mode_of_payment)
return mode_of_payments
def get_invoices(filters):
conditions = get_conditions(filters)
return frappe.db.sql("""select a.name
from `tabSales Invoice` a
where a.docstatus = 1 and {conditions}""".format(conditions=conditions),
filters, as_dict=1)
def get_mode_of_payment_details(filters):
mode_of_payment_details = {}
invoice_list = get_invoices(filters)
invoice_list_names = ",".join(['"' + invoice['name'] + '"' for invoice in invoice_list])
if invoice_list:
inv_mop_detail = frappe.db.sql("""select a.owner, a.posting_date,
ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_amount) as paid_amount
from `tabSales Invoice` a, `tabSales Invoice Payment` b
where a.name = b.parent
and a.name in ({invoice_list_names})
group by a.owner, a.posting_date, mode_of_payment
union
select a.owner,a.posting_date,
ifnull(b.mode_of_payment, '') as mode_of_payment, sum(b.base_paid_amount) as paid_amount
from `tabSales Invoice` a, `tabPayment Entry` b,`tabPayment Entry Reference` c
where a.name = c.reference_name
and b.name = c.parent
and a.name in ({invoice_list_names})
group by a.owner, a.posting_date, mode_of_payment
union
select a.owner, a.posting_date,
ifnull(a.voucher_type,'') as mode_of_payment, sum(b.credit)
from `tabJournal Entry` a, `tabJournal Entry Account` b
where a.name = b.parent
and a.docstatus = 1
and b.reference_type = "Sales Invoice"
and b.reference_name in ({invoice_list_names})
group by a.owner, a.posting_date, mode_of_payment
""".format(invoice_list_names=invoice_list_names), as_dict=1)
for d in inv_mop_detail:
mode_of_payment_details.setdefault(d["owner"]+cstr(d["posting_date"]), []).append((d.mode_of_payment,d.paid_amount))
return mode_of_payment_details

View File

@ -252,6 +252,9 @@ def add_ac(args=None):
if not ac.parent_account: if not ac.parent_account:
ac.parent_account = args.get("parent") ac.parent_account = args.get("parent")
if ac.is_root:
ac.parent_account=''
ac.old_parent = "" ac.old_parent = ""
ac.freeze_account = "No" ac.freeze_account = "No"
if cint(ac.get("is_root")): if cint(ac.get("is_root")):
@ -576,16 +579,17 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
outstanding_invoices = [] outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") precision = frappe.get_precision("Sales Invoice", "outstanding_amount")
if party_type == "Customer" or party_type == "Student": if party_type in ("Customer", "Student"):
dr_or_cr = "debit_in_account_currency - credit_in_account_currency" dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency" payment_dr_or_cr = "payment_gl_entry.credit_in_account_currency - payment_gl_entry.debit_in_account_currency"
else: else:
dr_or_cr = "credit_in_account_currency - debit_in_account_currency" dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency" payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
invoice_list = frappe.db.sql(""" invoice_list = frappe.db.sql("""
select select
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount, due_date, voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount,
( (
select ifnull(sum({payment_dr_or_cr}), 0) select ifnull(sum({payment_dr_or_cr}), 0)
from `tabGL Entry` payment_gl_entry from `tabGL Entry` payment_gl_entry
@ -596,7 +600,6 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
and payment_gl_entry.party_type = invoice_gl_entry.party_type and payment_gl_entry.party_type = invoice_gl_entry.party_type
and payment_gl_entry.party = invoice_gl_entry.party and payment_gl_entry.party = invoice_gl_entry.party
and payment_gl_entry.account = invoice_gl_entry.account and payment_gl_entry.account = invoice_gl_entry.account
and payment_gl_entry.due_date = invoice_gl_entry.due_date
and {payment_dr_or_cr} > 0 and {payment_dr_or_cr} > 0
) as payment_amount ) as payment_amount
from from
@ -608,10 +611,11 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
and ((voucher_type = 'Journal Entry' and ((voucher_type = 'Journal Entry'
and (against_voucher = '' or against_voucher is null)) and (against_voucher = '' or against_voucher is null))
or (voucher_type not in ('Journal Entry', 'Payment Entry'))) or (voucher_type not in ('Journal Entry', 'Payment Entry')))
group by voucher_type, voucher_no, due_date group by voucher_type, voucher_no
having (invoice_amount - payment_amount) > 0.005 having (invoice_amount - payment_amount) > 0.005
order by posting_date, name, due_date""".format( order by posting_date, name""".format(
dr_or_cr=dr_or_cr, dr_or_cr=dr_or_cr,
invoice = invoice,
payment_dr_or_cr=payment_dr_or_cr, payment_dr_or_cr=payment_dr_or_cr,
condition=condition or "" condition=condition or ""
), { ), {
@ -621,12 +625,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
}, as_dict=True) }, as_dict=True)
for d in invoice_list: for d in invoice_list:
due_date = d.due_date or ( due_date = frappe.db.get_value(d.voucher_type, d.voucher_no,
frappe.db.get_value( "posting_date" if party_type == "Employee" else "due_date")
d.voucher_type, d.voucher_no,
"posting_date" if party_type == "Employee" else "due_date"
)
)
outstanding_invoices.append( outstanding_invoices.append(
frappe._dict({ frappe._dict({
@ -704,7 +704,7 @@ def get_children(doctype, parent, company, is_root=False):
return acc return acc
def create_payment_gateway_account(gateway): def create_payment_gateway_account(gateway):
from erpnext.setup.setup_wizard.setup_wizard import create_bank_account from erpnext.setup.setup_wizard.operations.company_setup import create_bank_account
company = frappe.db.get_value("Global Defaults", None, "default_company") company = frappe.db.get_value("Global Defaults", None, "default_company")
if not company: if not company:

View File

@ -1,7 +1,55 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt // For license information, please see license.txt
frappe.provide("erpnext.crop");
frappe.ui.form.on('Crop', { frappe.ui.form.on('Crop', {
validate: (frm) => { refresh: (frm) => {
frm.fields_dict.materials_required.grid.set_column_disp('bom_no', false);
} }
}); });
frappe.ui.form.on("BOM Item", {
item_code: (frm, cdt, cdn) => {
erpnext.crop.update_item_rate_uom(frm, cdt, cdn);
},
qty: (frm, cdt, cdn) => {
erpnext.crop.update_item_qty_amount(frm, cdt, cdn);
},
rate: (frm, cdt, cdn) => {
erpnext.crop.update_item_qty_amount(frm, cdt, cdn);
}
});
erpnext.crop.update_item_rate_uom = function(frm, cdt, cdn) {
let material_list = ['materials_required', 'produce', 'byproducts'];
material_list.forEach((material) => {
frm.doc[material].forEach((item, index) => {
if (item.name == cdn && item.item_code){
frappe.call({
method:'erpnext.agriculture.doctype.crop.crop.get_item_details',
args: {
item_code: item.item_code
},
callback: (r) => {
frappe.model.set_value('BOM Item', item.name, 'uom', r.message.uom);
frappe.model.set_value('BOM Item', item.name, 'rate', r.message.rate);
}
});
}
});
});
};
erpnext.crop.update_item_qty_amount = function(frm, cdt, cdn) {
let material_list = ['materials_required', 'produce', 'byproducts'];
material_list.forEach((material) => {
frm.doc[material].forEach((item, index) => {
if (item.name == cdn){
if (!frappe.model.get_value('BOM Item', item.name, 'qty'))
frappe.model.set_value('BOM Item', item.name, 'qty', 1);
frappe.model.set_value('BOM Item', item.name, 'amount', item.qty * item.rate);
}
});
});
};

View File

@ -15,4 +15,9 @@ class Crop(Document):
frappe.throw("Start day is greater than end day in task '{0}'".format(task.subject)) frappe.throw("Start day is greater than end day in task '{0}'".format(task.subject))
# to calculate the period of the Crop Cycle # to calculate the period of the Crop Cycle
if task.end_day > max_period: max_period = task.end_day if task.end_day > max_period: max_period = task.end_day
if max_period > self.period: self.period = max_period if max_period > self.period: self.period = max_period
@frappe.whitelist()
def get_item_details(item_code):
item = frappe.get_doc('Item', item_code)
return { "uom": item.stock_uom, "rate": item.valuation_rate }

View File

@ -15,7 +15,7 @@ frappe.ui.form.on('Crop Cycle', {
output[doctype].forEach( (analysis_doc) => { output[doctype].forEach( (analysis_doc) => {
let point_to_be_tested = JSON.parse(analysis_doc.location).features[0].geometry.coordinates; let point_to_be_tested = JSON.parse(analysis_doc.location).features[0].geometry.coordinates;
let poly_of_land = JSON.parse(land_doc.location).features[0].geometry.coordinates[0]; let poly_of_land = JSON.parse(land_doc.location).features[0].geometry.coordinates[0];
if (test_analysis_position(point_to_be_tested, poly_of_land)){ if (is_in_land_unit(point_to_be_tested, poly_of_land)){
obj_to_append[analysis_doctypes_docs[analysis_doctypes.indexOf(doctype)]].push(analysis_doc.name); obj_to_append[analysis_doctypes_docs[analysis_doctypes.indexOf(doctype)]].push(analysis_doc.name);
} }
}); });
@ -28,7 +28,7 @@ frappe.ui.form.on('Crop Cycle', {
} }
}); });
function test_analysis_position(point, vs) { function is_in_land_unit(point, vs) {
// ray-casting algorithm based on // ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html

View File

@ -599,6 +599,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:!doc.__islocal",
"description": "List of diseases detected on the field. When selected it'll automatically add a list of tasks to deal with the disease ", "description": "List of diseases detected on the field. When selected it'll automatically add a list of tasks to deal with the disease ",
"fieldname": "section_break_14", "fieldname": "section_break_14",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -790,7 +791,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-06 01:47:26.656870", "modified": "2017-12-18 12:54:29.071743",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Agriculture", "module": "Agriculture",
"name": "Crop Cycle", "name": "Crop Cycle",

View File

@ -12,11 +12,37 @@ class CropCycle(Document):
if self.is_new(): if self.is_new():
crop = frappe.get_doc('Crop', self.crop) crop = frappe.get_doc('Crop', self.crop)
self.create_project(crop.period, crop.agriculture_task) self.create_project(crop.period, crop.agriculture_task)
if not self.project: if not self.crop_spacing_uom:
self.project = self.name self.crop_spacing_uom = crop.crop_spacing_uom
for detected_disease in self.detected_disease: if not self.row_spacing_uom:
disease = frappe.get_doc('Disease', detected_disease.disease) self.row_spacing_uom = crop.row_spacing_uom
self.create_task(disease.treatment_task, self.name, detected_disease.start_date) if not self.project:
self.project = self.name
disease = []
for detected_disease in self.detected_disease:
disease.append(detected_disease.name)
if disease != []:
self.update_disease(disease)
else:
old_disease, new_disease = [], []
for detected_disease in self.detected_disease:
new_disease.append(detected_disease.name)
for detected_disease in self.get_doc_before_save().get('detected_disease'):
old_disease.append(detected_disease.name)
if list(set(new_disease)-set(old_disease)) != []:
self.update_disease(list(set(new_disease)-set(old_disease)))
frappe.msgprint("All tasks for the detected diseases were imported")
def update_disease(self, disease_hashes):
new_disease = []
for disease in self.detected_disease:
for disease_hash in disease_hashes:
if disease.name == disease_hash:
self.import_disease_tasks(disease.disease, disease.start_date)
def import_disease_tasks(self, disease, start_date):
disease_doc = frappe.get_doc('Disease', disease)
self.create_task(disease_doc.treatment_task, self.name, start_date)
def create_project(self, period, crop_tasks): def create_project(self, period, crop_tasks):
project = frappe.new_doc("Project") project = frappe.new_doc("Project")
@ -59,4 +85,19 @@ class CropCycle(Document):
return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates') return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('coordinates')
def get_geometry_type(self, doc): def get_geometry_type(self, doc):
return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type') return ast.literal_eval(doc.location).get('features')[0].get('geometry').get('type')
def is_in_land_unit(self, point, vs):
x, y = point
inside = False
j = len(vs)-1
i = 0
while i < len(vs):
xi, yi = vs[i]
xj, yj = vs[j]
intersect = ((yi > y) != (yj > y)) and (x < (xj - xi) * (y - yi) / (yj - yi) + xi)
if intersect:
inside = not inside
i = j
j += 1
return inside

View File

@ -102,66 +102,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Treatment Task",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -222,6 +162,66 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Treatment Task",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -234,7 +234,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-04 19:20:19.352479", "modified": "2017-12-07 16:24:22.923154",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Agriculture", "module": "Agriculture",
"name": "Disease", "name": "Disease",

View File

@ -9,6 +9,13 @@ frappe.ui.form.on('Land Unit', {
setup: function(frm) { setup: function(frm) {
frm.add_fetch("parent_land_unit", "latitude", "latitude"); frm.add_fetch("parent_land_unit", "latitude", "latitude");
frm.add_fetch("parent_land_unit", "longitude", "longitude"); frm.add_fetch("parent_land_unit", "longitude", "longitude");
frm.set_query("parent_land_unit", function() {
return {
"filters": {
"is_group": 1
}
};
});
}, },
onload_post_render(frm){ onload_post_render(frm){
@ -20,12 +27,4 @@ frappe.ui.form.on('Land Unit', {
frm.doc.longitude = frm.fields_dict.location.map.getCenter()['lng']; frm.doc.longitude = frm.fields_dict.location.map.getCenter()['lng'];
} }
}, },
refresh: function(frm) {
if(!frm.doc.parent_land_unit) {
frm.set_read_only();
frm.set_intro(__("This is a root land unit and cannot be edited."));
} else {
frm.set_intro(null);
}
},
}); });

View File

@ -431,7 +431,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -502,7 +502,7 @@
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 1,
"columns": 0, "columns": 0,
"fieldname": "section_break_17", "fieldname": "section_break_17",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -513,6 +513,7 @@
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Linked Analysis",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -631,7 +632,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-05 10:57:33.108504", "modified": "2017-12-14 18:16:15.124188",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Agriculture", "module": "Agriculture",
"name": "Land Unit", "name": "Land Unit",
@ -679,7 +680,7 @@
"write": 1 "write": 1
} }
], ],
"quick_entry": 0, "quick_entry": 1,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"show_name_in_global_search": 1, "show_name_in_global_search": 1,

View File

@ -10,6 +10,7 @@ import math
from frappe import _ from frappe import _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
from frappe.utils import flt
# from frappe.model.document import Document # from frappe.model.document import Document
RADIUS = 6378137 RADIUS = 6378137
@ -31,6 +32,7 @@ class LandUnit(NestedSet):
ancestor_features[index] = json.loads(feature) ancestor_features[index] = json.loads(feature)
ancestor_doc.set_location_value(features = ancestor_features) ancestor_doc.set_location_value(features = ancestor_features)
ancestor_doc.db_set(fieldname='area', value=ancestor_doc.get('area')-self.get('area'),commit=True) ancestor_doc.db_set(fieldname='area', value=ancestor_doc.get('area')-self.get('area'),commit=True)
super(LandUnit, self).on_update()
def validate(self): def validate(self):
if not self.is_new(): if not self.is_new():
@ -39,10 +41,10 @@ class LandUnit(NestedSet):
else: else:
features = json.loads(self.get('location')).get('features') features = json.loads(self.get('location')).get('features')
new_area = compute_area(features) new_area = compute_area(features)
self.area_difference = new_area - self.area self.area_difference = new_area - flt(self.area)
self.area = new_area self.area = new_area
if self.get('parent'): if self.get('parent_land_unit'):
ancestors = self.get_ancestors() ancestors = self.get_ancestors()
self_features = self.add_child_property() self_features = self.add_child_property()
self_features = set(self_features) self_features = set(self_features)
@ -78,7 +80,6 @@ class LandUnit(NestedSet):
def on_update(self): def on_update(self):
super(LandUnit, self).on_update() super(LandUnit, self).on_update()
self.validate_one_root()
def add_child_property(self): def add_child_property(self):
location = self.get('location') location = self.get('location')
@ -118,7 +119,7 @@ def compute_area(features):
layer_area += polygon_area(coords = feature.get('geometry').get('coordinates')) layer_area += polygon_area(coords = feature.get('geometry').get('coordinates'))
elif feature.get('geometry').get('type') == 'Point' and feature.get('properties').get('point_type') == 'circle': elif feature.get('geometry').get('type') == 'Point' and feature.get('properties').get('point_type') == 'circle':
layer_area += math.pi * math.pow(feature.get('properties').get('radius'), 2) layer_area += math.pi * math.pow(feature.get('properties').get('radius'), 2)
return layer_area return flt(layer_area)
def rad(angle_in_degrees): def rad(angle_in_degrees):
return angle_in_degrees*math.pi/180 return angle_in_degrees*math.pi/180
@ -162,4 +163,18 @@ def ring_area(coords):
area = area * RADIUS * RADIUS / 2 area = area * RADIUS * RADIUS / 2
return area return area
@frappe.whitelist()
def get_children(doctype, parent, is_root=False):
if is_root:
parent = ''
land_units = frappe.db.sql("""select name as value,
is_group as expandable
from `tabLand Unit`
where ifnull(`parent_land_unit`,'') = %s
order by name""", (parent), as_dict=1)
# return nodes
return land_units

View File

@ -1,15 +1,30 @@
frappe.treeview_settings["Land Unit"] = { frappe.treeview_settings["Land Unit"] = {
get_tree_nodes: "erpnext.agriculture.doctype.land_unit.land_unit.get_children",
ignore_fields:["parent_land_unit"], ignore_fields:["parent_land_unit"],
get_tree_root: false,
disable_add_node: true, disable_add_node: true,
root_label: "All Land Units",
onload: function(me) {
me.make_tree();
},
toolbar: [ toolbar: [
{ toggle_btn: true }, { toggle_btn: true },
{ {
label:__("Add Child"), label:__("Edit"),
condition: function(node) { return (node.label!='All Land Units'); },
click: function(node) { click: function(node) {
frappe.set_route('Form', 'Land Unit', node.data.value);
}
},
{
label:__("Add Child"),
condition: function(node) { return node.expandable; },
click: function(node) {
if(node.label=='All Land Units') node.label='';
var lu = frappe.new_doc("Land Unit", { var lu = frappe.new_doc("Land Unit", {
"parent_land_unit": node.label "parent_land_unit": node.label
}) });
} }
} }
], ],
} };

View File

@ -12,7 +12,6 @@ QUnit.test("test: Land Unit", function (assert) {
// insert a new Land Unit // insert a new Land Unit
() => frappe.tests.make('Land Unit', [ () => frappe.tests.make('Land Unit', [
// values to be set // values to be set
{parent_land_unit: 'All Land Units'},
{land_unit_name: 'Basil Farm'} {land_unit_name: 'Basil Farm'}
]), ]),
() => { () => {

View File

@ -21,6 +21,6 @@ class TestLandUnit(unittest.TestCase):
temp['features'][0]['properties']['feature_of'] = land_unit temp['features'][0]['properties']['feature_of'] = land_unit
formatted_land_units.extend(temp['features']) formatted_land_units.extend(temp['features'])
formatted_land_unit_string = str(formatted_land_units) formatted_land_unit_string = str(formatted_land_units)
all_land_units = frappe.get_doc('Land Unit', 'All Land Units') test_land = frappe.get_doc('Land Unit', 'Test Land')
self.assertEquals(formatted_land_unit_string, str(json.loads(all_land_units.get('location'))['features'])) self.assertEquals(formatted_land_unit_string, str(json.loads(test_land.get('location'))['features']))
self.assertEquals(area, all_land_units.get('area')) self.assertEquals(area, test_land.get('area'))

View File

@ -1,10 +1,16 @@
[ [
{
"doctype": "Land Unit",
"land_unit_name": "Test Land",
"is_group": 1,
"is_container": 1
},
{ {
"doctype": "Land Unit", "doctype": "Land Unit",
"land_unit_name": "Basil Farm", "land_unit_name": "Basil Farm",
"location": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"point_type\":\"circle\",\"radius\":884.5625420736483},\"geometry\":{\"type\":\"Point\",\"coordinates\":[72.875834,19.100566]}}]}", "location": "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{\"point_type\":\"circle\",\"radius\":884.5625420736483},\"geometry\":{\"type\":\"Point\",\"coordinates\":[72.875834,19.100566]}}]}",
"parent_land_unit": "All Land Units", "parent_land_unit": "Test Land",
"parent": "All Land Units", "parent": "Test Land",
"is_group": 1, "is_group": 1,
"is_container": 1 "is_container": 1
}, },

View File

@ -4,12 +4,13 @@ from frappe import _
from erpnext.setup.utils import insert_record from erpnext.setup.utils import insert_record
def setup_agriculture(): def setup_agriculture():
if frappe.get_all('Agriculture Analysis Criteria'):
# already setup
return
create_agriculture_data()
def create_agriculture_data():
records = [ records = [
dict(
doctype="Land Unit",
land_unit_name="All Land Units",
is_group=1,
is_container=1),
dict( dict(
doctype='Item Group', doctype='Item Group',
item_group_name='Fertilizer', item_group_name='Fertilizer',
@ -182,7 +183,7 @@ def setup_agriculture():
linked_doctype='Soil Analysis'), linked_doctype='Soil Analysis'),
dict( dict(
doctype='Agriculture Analysis Criteria', doctype='Agriculture Analysis Criteria',
title='pH', title='Soil pH',
standard=1, standard=1,
linked_doctype='Soil Analysis'), linked_doctype='Soil Analysis'),
dict( dict(
@ -272,7 +273,7 @@ def setup_agriculture():
linked_doctype='Soil Analysis'), linked_doctype='Soil Analysis'),
dict( dict(
doctype='Agriculture Analysis Criteria', doctype='Agriculture Analysis Criteria',
title='pH', title='Water pH',
standard=1, standard=1,
linked_doctype='Water Analysis'), linked_doctype='Water Analysis'),
dict( dict(
@ -424,11 +425,6 @@ def setup_agriculture():
doctype='Agriculture Analysis Criteria', doctype='Agriculture Analysis Criteria',
title='Degree Days', title='Degree Days',
standard=1, standard=1,
linked_doctype='Weather'), linked_doctype='Weather')
dict(
doctype='Agriculture Analysis Criteria',
title='Degree Days',
standard=1,
linked_doctype='Water Analysis')
] ]
insert_record(records) insert_record(records)

View File

@ -175,7 +175,7 @@ frappe.ui.form.on('Asset', {
"item_code": frm.doc.item_code, "item_code": frm.doc.item_code,
"company": frm.doc.company "company": frm.doc.company
}, },
method: "erpnext.accounts.doctype.asset.asset.make_sales_invoice", method: "erpnext.assets.doctype.asset.asset.make_sales_invoice",
callback: function(r) { callback: function(r) {
var doclist = frappe.model.sync(r.message); var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name); frappe.set_route("Form", doclist[0].doctype, doclist[0].name);

View File

@ -154,7 +154,7 @@
"label": "Status", "label": "Status",
"length": 0, "length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Repair\nOut of Order", "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -193,7 +193,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -1221,7 +1221,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-12-01 15:11:47.466859", "modified": "2017-12-19 12:58:44.137460",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset", "name": "Asset",

View File

@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt, add_months, cint, nowdate, getdate from frappe.utils import flt, add_months, cint, nowdate, getdate, today
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_fixed_asset_account from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_fixed_asset_account
from erpnext.assets.doctype.asset.depreciation \ from erpnext.assets.doctype.asset.depreciation \
@ -174,7 +174,7 @@ class Asset(Document):
def set_status(self, status=None): def set_status(self, status=None):
'''Get and update status''' '''Get and update status'''
if not status: if not status:
status = self.get_status() status = self.get_status()
self.db_set("status", status) self.db_set("status", status)
def get_status(self): def get_status(self):
@ -193,6 +193,16 @@ class Asset(Document):
status = "Cancelled" status = "Cancelled"
return status return status
def update_maintenance_status():
assets = frappe.get_all('Asset', filters = {'docstatus': 1, 'maintenance_required': 1})
for asset in assets:
asset = frappe.get_doc("Asset", asset.name)
if frappe.db.exists('Asset Maintenance Task', {'parent': asset.name, 'next_due_date': today()}):
asset.set_status('In Maintenance')
if frappe.db.exists('Asset Repair', {'asset_name': asset.name, 'repair_status': 'Pending'}):
asset.set_status('Out of Order')
@frappe.whitelist() @frappe.whitelist()
def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date): def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date):
pi = frappe.new_doc("Purchase Invoice") pi = frappe.new_doc("Purchase Invoice")

View File

@ -225,7 +225,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"modified": "2015-08-25 04:55:06.052342", "modified": "2017-12-27 15:20:06.052342",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying Settings", "name": "Buying Settings",

View File

@ -12,6 +12,15 @@ frappe.ui.form.on("Purchase Order", {
'Purchase Invoice': 'Invoice', 'Purchase Invoice': 'Invoice',
'Stock Entry': 'Material to Supplier' 'Stock Entry': 'Material to Supplier'
} }
frm.set_query("reserve_warehouse", "supplied_items", function() {
return {
filters: {
"company": frm.doc.company,
"is_group": 0
}
}
});
}, },
onload: function(frm) { onload: function(frm) {
@ -21,22 +30,17 @@ frappe.ui.form.on("Purchase Order", {
return erpnext.queries.warehouse(frm.doc); return erpnext.queries.warehouse(frm.doc);
}); });
frappe.db.get_value('Buying Settings', {name: 'Buying Settings'}, 'disable_fetch_last_purchase_rate', (r) => {
value = r && cint(r.disable_fetch_last_purchase_rate);
frm.toggle_display('get_last_purchase_rate', !value);
});
frm.set_indicator_formatter('item_code', frm.set_indicator_formatter('item_code',
function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" }) function(doc) { return (doc.qty<=doc.received_qty) ? "green" : "orange" })
}, },
}); });
frappe.ui.form.on("Purchase Order Item", { frappe.ui.form.on("Purchase Order Item", {
item_code: function(frm) {
frappe.call({
method: "get_last_purchase_rate",
doc: frm.doc,
callback: function(r, rt) {
frm.trigger('calculate_taxes_and_totals');
}
})
},
schedule_date: function(frm, cdt, cdn) { schedule_date: function(frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if (row.schedule_date) { if (row.schedule_date) {
@ -289,7 +293,8 @@ cur_frm.fields_dict['items'].grid.get_field('bom').get_query = function(doc, cdt
filters: [ filters: [
['BOM', 'item', '=', d.item_code], ['BOM', 'item', '=', d.item_code],
['BOM', 'is_active', '=', '1'], ['BOM', 'is_active', '=', '1'],
['BOM', 'docstatus', '=', '1'] ['BOM', 'docstatus', '=', '1'],
['BOM', 'company', '=', doc.company]
] ]
} }
} }

View File

@ -41,11 +41,11 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "{supplier_name}", "default": "{supplier_name}",
@ -292,40 +292,40 @@
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 1, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "", "default": "",
"fieldname": "schedule_date", "fieldname": "schedule_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0, "in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Reqd By Date", "label": "Reqd By Date",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
@ -1238,6 +1238,37 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
"fieldname": "get_last_purchase_rate",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Get last purchase rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -2552,7 +2583,7 @@
"options": "Payment Schedule", "options": "Payment Schedule",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
@ -3071,6 +3102,38 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplied_items_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supplied Items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "supplied_items", "fieldname": "supplied_items",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@ -3261,7 +3324,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-29 14:06:33.636401", "modified": "2018-01-05 14:44:56.132189",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -11,9 +11,8 @@ from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.item.item import get_last_purchase_details from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty from erpnext.stock.stock_balance import update_bin_qty, get_ordered_qty
from frappe.desk.notifications import clear_doctype_notifications from frappe.desk.notifications import clear_doctype_notifications
from erpnext.buying.utils import (validate_for_items, check_for_closed_status, from erpnext.buying.utils import validate_for_items, check_for_closed_status
update_last_purchase_rate) from erpnext.stock.utils import get_bin
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@ -72,8 +71,10 @@ class PurchaseOrder(BuyingController):
def validate_supplier(self): def validate_supplier(self):
prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos') prevent_po = frappe.db.get_value("Supplier", self.supplier, 'prevent_pos')
if prevent_po: if prevent_po:
standing = frappe.db.get_value("Supplier Scorecard",self.supplier, 'status') standing = frappe.db.get_value("Supplier Scorecard", self.supplier, 'status')
frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.").format(self.supplier, standing)) if standing:
frappe.throw(_("Purchase Orders are not allowed for {0} due to a scorecard standing of {1}.")
.format(self.supplier, standing))
warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos') warn_po = frappe.db.get_value("Supplier", self.supplier, 'warn_pos')
if warn_po: if warn_po:
@ -112,27 +113,26 @@ class PurchaseOrder(BuyingController):
def get_last_purchase_rate(self): def get_last_purchase_rate(self):
"""get last purchase rates for all items""" """get last purchase rates for all items"""
if cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): return
if not cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): conversion_rate = flt(self.get('conversion_rate')) or 1.0
conversion_rate = flt(self.get('conversion_rate')) or 1.0 for d in self.get("items"):
if d.item_code:
last_purchase_details = get_last_purchase_details(d.item_code, self.name)
if last_purchase_details:
d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] *
(flt(d.conversion_factor) or 1.0))
d.discount_percentage = last_purchase_details['discount_percentage']
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
d.price_list_rate = d.base_price_list_rate / conversion_rate
d.rate = d.base_rate / conversion_rate
d.last_purchase_rate = d.rate
else:
for d in self.get("items"): item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
if d.item_code: if item_last_purchase_rate:
last_purchase_details = get_last_purchase_details(d.item_code, self.name) d.base_price_list_rate = d.base_rate = d.price_list_rate \
= d.rate = d.last_purchase_rate = item_last_purchase_rate
if last_purchase_details:
d.base_price_list_rate = (last_purchase_details['base_price_list_rate'] *
(flt(d.conversion_factor) or 1.0))
d.discount_percentage = last_purchase_details['discount_percentage']
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
d.price_list_rate = d.base_price_list_rate / conversion_rate
d.last_purchase_rate = d.base_rate / conversion_rate
else:
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
if item_last_purchase_rate:
d.base_price_list_rate = d.base_rate = d.price_list_rate \
= d.last_purchase_rate = item_last_purchase_rate
# Check for Closed status # Check for Closed status
def check_for_closed_status(self): def check_for_closed_status(self):
@ -185,29 +185,39 @@ class PurchaseOrder(BuyingController):
self.set_status(update=True, status=status) self.set_status(update=True, status=status)
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
self.notify_update() self.notify_update()
clear_doctype_notifications(self) clear_doctype_notifications(self)
def on_submit(self): def on_submit(self):
super(PurchaseOrder, self).on_submit()
if self.is_against_so(): if self.is_against_so():
self.update_status_updater() self.update_status_updater()
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total) self.company, self.base_grand_total)
update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self): def on_cancel(self):
super(PurchaseOrder, self).on_cancel()
if self.is_against_so(): if self.is_against_so():
self.update_status_updater() self.update_status_updater()
if self.has_drop_ship_item(): if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order() self.update_delivered_qty_in_sales_order()
if self.is_subcontracted == "Yes":
self.update_reserved_qty_for_subcontract()
self.check_for_closed_status() self.check_for_closed_status()
frappe.db.set(self,'status','Cancelled') frappe.db.set(self,'status','Cancelled')
@ -218,8 +228,6 @@ class PurchaseOrder(BuyingController):
self.update_requested_qty() self.update_requested_qty()
self.update_ordered_qty() self.update_ordered_qty()
update_last_purchase_rate(self, is_submit = 0)
def on_update(self): def on_update(self):
pass pass
@ -257,6 +265,27 @@ class PurchaseOrder(BuyingController):
if item.delivered_by_supplier == 1: if item.delivered_by_supplier == 1:
item.received_qty = item.qty item.received_qty = item.qty
def update_reserved_qty_for_subcontract(self):
for d in self.supplied_items:
if d.rm_item_code:
stock_bin = get_bin(d.rm_item_code, d.reserve_warehouse)
stock_bin.update_reserved_qty_for_sub_contracting()
def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0):
"""get last purchase rate for an item"""
if cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): return
conversion_rate = flt(conversion_rate) or 1.0
last_purchase_details = get_last_purchase_details(item_code, name)
if last_purchase_details:
last_purchase_rate = (last_purchase_details['base_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate
return last_purchase_rate
else:
item_last_purchase_rate = frappe.db.get_value("Item", item_code, "last_purchase_rate")
if item_last_purchase_rate:
return item_last_purchase_rate
@frappe.whitelist() @frappe.whitelist()
def close_or_unclose_purchase_orders(names, status): def close_or_unclose_purchase_orders(names, status):
if not frappe.has_permission("Purchase Order", "write"): if not frappe.has_permission("Purchase Order", "write"):

View File

@ -6,8 +6,8 @@ import unittest
import frappe import frappe
import frappe.defaults import frappe.defaults
from frappe.utils import flt, add_days, nowdate from frappe.utils import flt, add_days, nowdate
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt, make_purchase_invoice from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_stock_entry as make_subcontract_transfer_entry)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
class TestPurchaseOrder(unittest.TestCase): class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_receipt(self): def test_make_purchase_receipt(self):
@ -182,24 +182,129 @@ class TestPurchaseOrder(unittest.TestCase):
pi.insert() pi.insert()
self.assertTrue(pi.get('payment_schedule')) self.assertTrue(pi.get('payment_schedule'))
def test_reserved_qty_subcontract_po(self):
# Make stock available for raw materials
make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
qty=20, basic_rate=100)
bin1 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1)
# Submit PO
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
bin2 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname=["reserved_qty_for_sub_contract", "projected_qty"], as_dict=1)
self.assertEquals(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
self.assertEquals(bin2.projected_qty, bin1.projected_qty - 10)
# Create stock transfer
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, "_Test FG Item"))
se.to_warehouse = "_Test Warehouse 1 - _TC"
for d in se.get("items"):
if d.item_code == "_Test Item":
d.qty = 6
se.save()
se.submit()
bin3 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# close PO
po.update_status("Closed")
bin4 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Re-open PO
po.update_status("Submitted")
bin5 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# make Purchase Receipt against PO
pr = make_purchase_receipt(po.name)
pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
pr.save()
pr.submit()
bin6 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Cancel PR
pr.cancel()
bin7 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Make Purchase Invoice
pi = make_purchase_invoice(po.name)
pi.update_stock = 1
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.insert()
pi.submit()
bin8 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin8.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
# Cancel PR
pi.cancel()
bin9 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin9.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Cancel Stock Entry
se.cancel()
bin10 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin10.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10)
# Cancel PO
po.reload()
po.cancel()
bin11 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
fieldname="reserved_qty_for_sub_contract", as_dict=1)
self.assertEquals(bin11.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract)
def get_same_items(): def get_same_items():
return [ return [
{ {
"item_code": "_Test FG Item", "item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"qty": 1, "qty": 1,
"rate": 500, "rate": 500,
"schedule_date": add_days(nowdate(), 1) "schedule_date": add_days(nowdate(), 1)
}, },
{ {
"item_code": "_Test FG Item", "item_code": "_Test FG Item",
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"qty": 4, "qty": 4,
"rate": 500, "rate": 500,
"schedule_date": add_days(nowdate(), 1) "schedule_date": add_days(nowdate(), 1)
} }
] ]
def create_purchase_order(**args): def create_purchase_order(**args):
po = frappe.new_doc("Purchase Order") po = frappe.new_doc("Purchase Order")
@ -224,6 +329,10 @@ def create_purchase_order(**args):
if not args.do_not_save: if not args.do_not_save:
po.insert() po.insert()
if not args.do_not_submit: if not args.do_not_submit:
if po.is_subcontracted == "Yes":
supp_items = po.get("supplied_items")
for d in supp_items:
d.reserve_warehouse = args.warehouse or "_Test Warehouse - _TC"
po.submit() po.submit()
return po return po

View File

@ -24,7 +24,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
] ]
]}, ]},
{taxes_and_charges: 'TEST In State GST'} {taxes_and_charges: 'TEST In State GST - FT'}
]); ]);
}, },

View File

@ -1169,7 +1169,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -1259,7 +1259,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -1897,7 +1897,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-30 14:17:15.817754", "modified": "2017-12-14 09:36:40.837027",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item", "name": "Purchase Order Item",

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"beta": 0, "beta": 0,
@ -10,16 +11,20 @@
"editable_grid": 1, "editable_grid": 1,
"fields": [ "fields": [
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 2,
"fieldname": "main_item_code", "fieldname": "main_item_code",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Item Code", "label": "Item Code",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -29,6 +34,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -36,16 +42,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 2,
"fieldname": "rm_item_code", "fieldname": "rm_item_code",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Raw Material Item Code", "label": "Raw Material Item Code",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -55,6 +65,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -62,16 +73,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 2,
"fieldname": "required_qty", "fieldname": "required_qty",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Supplied Qty", "label": "Supplied Qty",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -81,6 +96,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -88,16 +104,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 2,
"fieldname": "rate", "fieldname": "rate",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Rate", "label": "Rate",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -108,6 +128,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -115,16 +136,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "amount", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount", "label": "Amount",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -135,6 +160,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -142,16 +168,49 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bom_detail_no", "fieldname": "bom_detail_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "BOM Detail No", "label": "BOM Detail No",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -161,6 +220,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -168,16 +228,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "reference_name", "fieldname": "reference_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference Name", "label": "Reference Name",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -187,6 +251,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -194,16 +259,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "conversion_factor", "fieldname": "conversion_factor",
"fieldtype": "Float", "fieldtype": "Float",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Conversion Factor", "label": "Conversion Factor",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -213,6 +282,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -220,16 +290,20 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "stock_uom", "fieldname": "stock_uom",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_global_search": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Stock Uom", "label": "Stock Uom",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -240,6 +314,38 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "reserve_warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Reserve Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -247,17 +353,17 @@
"unique": 0 "unique": 0
} }
], ],
"has_web_view": 0,
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 1, "hide_toolbar": 1,
"idx": 1, "idx": 1,
"image_view": 0, "image_view": 0,
"in_create": 0, "in_create": 0,
"in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-07-11 03:28:05.533063", "modified": "2018-01-05 14:47:15.400785",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item Supplied", "name": "Purchase Order Item Supplied",
@ -266,5 +372,7 @@
"quick_entry": 0, "quick_entry": 0,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0 "track_seen": 0
} }

View File

@ -941,8 +941,8 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-08-31 16:10:44.049915", "modified": "2017-12-26 04:50:15.317590",
"modified_by": "tundebabzy@gmail.com", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier", "name": "Supplier",
"name_case": "Title Case", "name_case": "Title Case",

View File

@ -17,7 +17,7 @@ QUnit.test("test: supplier quotation with taxes and charges", function(assert) {
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))}, {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
] ]
]}, ]},
{taxes_and_charges:'TEST In State GST'}, {taxes_and_charges:'TEST In State GST - FT'},
]); ]);
}, },
() => {supplier_quotation_name = cur_frm.doc.name;}, () => {supplier_quotation_name = cur_frm.doc.name;},

View File

@ -1072,7 +1072,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -1162,7 +1162,7 @@
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 1,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
@ -1614,7 +1614,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-11-30 14:22:10.542868", "modified": "2017-12-14 09:37:47.427897",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation Item", "name": "Supplier Quotation Item",

View File

@ -0,0 +1,61 @@
- Account Numbers
- Default Chart of accounts with account number
- Set account number for your existing chart of accounts
- Payment Terms
- Create Sales Invoice with multiple due dates and payment slab
- Agriculture (New Domain)
- Manage Crop, Crop Cycle, Land Unit, Disease and Fertilizer records
- Maintain records of Plant / Soil / Water Analysis
- Non Profits (New Domain)
- Manage records of Members, Donors, Volunteers and Chapters
- Portal for Grant Application
- Delivery Trip
- Track each of your delivery trips with their associated stops and timing
- Item Variants Update
- Create variants from Quick Entry dialog
- Create multiple variants from a single screen
- Shipping Rule
- Now available in Buying cycle
- Apply based on Net Weight / Fixed Amount / Net Total
- Updated POS
- Single POS Profile for multiple Users
- Sales Payment Summary (X & Z) report
- Fixed multiple bugs
- Employee Advance
- Manage Advances given to your employee and adjust with Expense Claim
- Payroll Entry
- Maintain records of each payroll processing
- Deprecated Process Payroll tool
- Schools to Education
- School module is renamed to Education
- Opening Invoice Tool
- A new tool to create opening invoices
- Asset Maintenance
- Maintain records of Asset Maintenance and Asset Repair
- Employee Tree
- Employee document now has a tree view, so you can create your organisation chart based on it
- Task Tree
- Task also has a Tree view now
- Education module update
- Course Scheduling Tool
- Batch selection based on earlier expiry date of the batch
- Invoice GL Entry based on rounded total (instead of grand total)
- Multiple UOM in Material Request

View File

@ -134,6 +134,14 @@ def get_data():
"link": "pos", "link": "pos",
"label": _("POS") "label": _("POS")
}, },
{
"module_name": "Leaderboard",
"color": "#589494",
"icon": "octicon octicon-graph",
"type": "page",
"link": "leaderboard",
"label": _("Leaderboard")
},
{ {
"module_name": "Projects", "module_name": "Projects",
"color": "#8e44ad", "color": "#8e44ad",
@ -292,18 +300,21 @@ def get_data():
"label": _("Hub") "label": _("Hub")
}, },
{ {
"module_name": "Data Import Tool", "module_name": "Data Import",
"color": "#7f8c8d", "color": "#FFF168",
"icon": "octicon octicon-circuit-board", "reverse": 1,
"type": "page", "doctype": "Data Import",
"link": "data-import-tool", "icon": "octicon octicon-cloud-upload",
"label": _("Data Import Tool") "label": _("Data Import"),
"link": "List/Data Import",
"type": "list"
}, },
{ {
"module_name": "Restaurant", "module_name": "Restaurant",
"color": "#EA81E8", "color": "#EA81E8",
"icon": "🍔", "icon": "🍔",
"_doctype": "Restaurant", "_doctype": "Restaurant",
"type": "list",
"link": "List/Restaurant", "link": "List/Restaurant",
"label": _("Restaurant"), "label": _("Restaurant"),
"hidden": 1 "hidden": 1
@ -322,7 +333,7 @@ def get_data():
"label": _("Crop"), "label": _("Crop"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-tree", "icon": "fa fa-tree",
"type": "link", "type": "list",
"link": "List/Crop", "link": "List/Crop",
"hidden": 1 "hidden": 1
}, },
@ -332,7 +343,7 @@ def get_data():
"label": _("Crop Cycle"), "label": _("Crop Cycle"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-circle-o-notch", "icon": "fa fa-circle-o-notch",
"type": "link", "type": "list",
"link": "List/Crop Cycle", "link": "List/Crop Cycle",
"hidden": 1 "hidden": 1
}, },
@ -342,7 +353,7 @@ def get_data():
"label": _("Fertilizer"), "label": _("Fertilizer"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-leaf", "icon": "fa fa-leaf",
"type": "link", "type": "list",
"link": "List/Fertilizer", "link": "List/Fertilizer",
"hidden": 1 "hidden": 1
}, },
@ -352,7 +363,7 @@ def get_data():
"label": _("Land Unit"), "label": _("Land Unit"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-map", "icon": "fa fa-map",
"type": "link", "type": "list",
"link": "List/Land Unit", "link": "List/Land Unit",
"hidden": 1 "hidden": 1
}, },
@ -362,7 +373,7 @@ def get_data():
"label": _("Disease"), "label": _("Disease"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "octicon octicon-bug", "icon": "octicon octicon-bug",
"type": "link", "type": "list",
"link": "List/Disease", "link": "List/Disease",
"hidden": 1 "hidden": 1
}, },
@ -372,7 +383,7 @@ def get_data():
"label": _("Plant Analysis"), "label": _("Plant Analysis"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-pagelines", "icon": "fa fa-pagelines",
"type": "link", "type": "list",
"link": "List/Plant Analysis", "link": "List/Plant Analysis",
"hidden": 1 "hidden": 1
}, },
@ -382,7 +393,7 @@ def get_data():
"label": _("Soil Analysis"), "label": _("Soil Analysis"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-flask", "icon": "fa fa-flask",
"type": "link", "type": "list",
"link": "List/Soil Analysis", "link": "List/Soil Analysis",
"hidden": 1 "hidden": 1
}, },
@ -392,7 +403,7 @@ def get_data():
"label": _("Soil Texture"), "label": _("Soil Texture"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "octicon octicon-beaker", "icon": "octicon octicon-beaker",
"type": "link", "type": "list",
"link": "List/Soil Texture", "link": "List/Soil Texture",
"hidden": 1 "hidden": 1
}, },
@ -402,7 +413,7 @@ def get_data():
"label": _("Water Analysis"), "label": _("Water Analysis"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-tint", "icon": "fa fa-tint",
"type": "link", "type": "list",
"link": "List/Water Analysis", "link": "List/Water Analysis",
"hidden": 1 "hidden": 1
}, },
@ -412,7 +423,7 @@ def get_data():
"label": _("Weather"), "label": _("Weather"),
"color": "#8BC34A", "color": "#8BC34A",
"icon": "fa fa-sun-o", "icon": "fa fa-sun-o",
"type": "link", "type": "list",
"link": "List/Weather", "link": "List/Weather",
"hidden": 1 "hidden": 1
}, },
@ -429,6 +440,7 @@ def get_data():
"color": "#E9AB17", "color": "#E9AB17",
"icon": "fa fa-gift", "icon": "fa fa-gift",
"_doctype": "Grant Application", "_doctype": "Grant Application",
"type": "list",
"link": "List/Grant Application", "link": "List/Grant Application",
"label": _("Grant Application"), "label": _("Grant Application"),
"hidden": 1 "hidden": 1
@ -439,6 +451,7 @@ def get_data():
"color": "#7F5A58", "color": "#7F5A58",
"icon": "fa fa-tint", "icon": "fa fa-tint",
"_doctype": "Donor", "_doctype": "Donor",
"type": "list",
"link": "List/Donor", "link": "List/Donor",
"label": _("Donor"), "label": _("Donor"),
"hidden": 1 "hidden": 1
@ -448,6 +461,7 @@ def get_data():
"color": "#7E587E", "color": "#7E587E",
"icon": "fa fa-angellist", "icon": "fa fa-angellist",
"_doctype": "Volunteer", "_doctype": "Volunteer",
"type": "list",
"link": "List/Volunteer", "link": "List/Volunteer",
"label": _("Volunteer"), "label": _("Volunteer"),
"hidden": 1 "hidden": 1
@ -457,6 +471,7 @@ def get_data():
"color": "#79BAEC", "color": "#79BAEC",
"icon": "fa fa-users", "icon": "fa fa-users",
"_doctype": "Member", "_doctype": "Member",
"type": "list",
"link": "List/Member", "link": "List/Member",
"label": _("Member"), "label": _("Member"),
"hidden": 1 "hidden": 1
@ -466,6 +481,7 @@ def get_data():
"color": "#3B9C9C", "color": "#3B9C9C",
"icon": "fa fa-handshake-o", "icon": "fa fa-handshake-o",
"_doctype": "Chapter", "_doctype": "Chapter",
"type": "list",
"link": "List/Chapter", "link": "List/Chapter",
"label": _("Chapter"), "label": _("Chapter"),
"hidden": 1 "hidden": 1

View File

@ -11,7 +11,7 @@ def get_data():
{ {
"type": "doctype", "type": "doctype",
"name": "Patient Appointment", "name": "Patient Appointment",
"description": _("Patient Appointment"), "label": _("Patient Appointment"),
}, },
{ {
"type": "doctype", "type": "doctype",
@ -43,7 +43,7 @@ def get_data():
{ {
"type": "doctype", "type": "doctype",
"name": "Lab Test", "name": "Lab Test",
"description": _("Results"), "label": _("Lab Test"),
}, },
{ {
"type": "doctype", "type": "doctype",
@ -53,7 +53,8 @@ def get_data():
{ {
"type": "report", "type": "report",
"name": "Lab Test Report", "name": "Lab Test Report",
"is_query_report": True "is_query_report": True,
"label": _("Lab Test Report"),
} }
] ]
}, },
@ -69,7 +70,7 @@ def get_data():
{ {
"type": "doctype", "type": "doctype",
"name": "Physician", "name": "Physician",
"label": "Physician", "label": _("Physician"),
}, },
{ {
"type": "doctype", "type": "doctype",
@ -100,57 +101,57 @@ def get_data():
{ {
"type": "doctype", "type": "doctype",
"name": "Medical Department", "name": "Medical Department",
"label": "Medical Department" "label": _("Medical Department"),
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Appointment Type", "name": "Appointment Type",
"description": _("Appointment Type Master"), "label": _("Appointment Type"),
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Prescription Dosage", "name": "Prescription Dosage",
"description": _("Prescription Dosage") "label": _("Prescription Dosage")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Prescription Duration", "name": "Prescription Duration",
"description": _("Prescription Period") "label": _("Prescription Duration")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Complaint", "name": "Complaint",
"description": _("Complaint") "label": _("Complaint")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Diagnosis", "name": "Diagnosis",
"description": _("Diagnosis") "label": _("Diagnosis")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Lab Test Sample", "name": "Lab Test Sample",
"description": _("Test Sample Master."), "label": _("Test Sample."),
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Lab Test UOM", "name": "Lab Test UOM",
"description": _("Lab Test UOM.") "label": _("Lab Test UOM.")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Antibiotic", "name": "Antibiotic",
"description": _("Antibiotic.") "label": _("Antibiotic.")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Sensitivity", "name": "Sensitivity",
"description": _("Sensitivity Naming.") "label": _("Sensitivity Naming.")
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Lab Test Template", "name": "Lab Test Template",
"description": _("Lab Test Configurations.") "label": _("Lab Test Template.")
} }
] ]
} }

View File

@ -121,6 +121,11 @@ def get_data():
{ {
"label": _("Expense Claims"), "label": _("Expense Claims"),
"items": [ "items": [
{
"type": "doctype",
"name": "Employee Advance",
"description": _("Manage advance amount given to the Employee"),
},
{ {
"type": "doctype", "type": "doctype",
"name": "Expense Claim", "name": "Expense Claim",

View File

@ -24,7 +24,7 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Report Builder"), "label": _("Report Builder"),
"youtube_id": "y0o5iYZOioU" "youtube_id": "TxJGUNarcQs"
}, },
] ]
@ -40,7 +40,7 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Opening Stock Balance"), "label": _("Opening Stock Balance"),
"youtube_id": "0yPgrtfeCTs" "youtube_id": "nlHX0ZZ84Lw"
}, },
{ {
"type": "help", "type": "help",
@ -55,7 +55,7 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Users and Permissions"), "label": _("Users and Permissions"),
"youtube_id": "fnBoRhBrwR4" "youtube_id": "8Slw1hsTmUI"
}, },
{ {
"type": "help", "type": "help",
@ -120,7 +120,7 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Sales Order to Payment"), "label": _("Sales Order to Payment"),
"youtube_id": "7AMq4lqkN4A" "youtube_id": "1eP90MWoDQM"
}, },
{ {
"type": "help", "type": "help",
@ -195,12 +195,12 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Material Request to Purchase Order"), "label": _("Material Request to Purchase Order"),
"youtube_id": "4TN9kPyfIqM" "youtube_id": "55Gk2j7Q8Zw"
}, },
{ {
"type": "help", "type": "help",
"label": _("Purchase Order to Payment"), "label": _("Purchase Order to Payment"),
"youtube_id": "EK65tLdVUDk" "youtube_id": "efFajTTQBa8"
}, },
{ {
"type": "help", "type": "help",
@ -261,7 +261,7 @@ def get_data():
{ {
"type": "help", "type": "help",
"label": _("Managing Projects"), "label": _("Managing Projects"),
"youtube_id": "egxIGwtoKI4" "youtube_id": "gCzShu9Niu4"
}, },
] ]
}, },

View File

@ -56,7 +56,12 @@ def get_data():
"name": "Stock Ageing", "name": "Stock Ageing",
"doctype": "Item", "doctype": "Item",
}, },
{
"type": "report",
"is_query_report": True,
"name": "Item Price Stock",
"doctype": "Item",
}
] ]
}, },
{ {

View File

@ -64,15 +64,14 @@ class AccountsController(TransactionBase):
def validate_invoice_documents_schedule(self): def validate_invoice_documents_schedule(self):
self.validate_payment_schedule_dates() self.validate_payment_schedule_dates()
self.set_due_date() self.set_due_date()
self.validate_invoice_portion()
self.set_payment_schedule() self.set_payment_schedule()
self.validate_payment_schedule_amount() self.validate_payment_schedule_amount()
self.validate_due_date() self.validate_due_date()
self.validate_advance_entries() self.validate_advance_entries()
def validate_non_invoice_documents_schedule(self): def validate_non_invoice_documents_schedule(self):
self.validate_invoice_portion()
self.set_payment_schedule() self.set_payment_schedule()
self.validate_payment_schedule_dates()
self.validate_payment_schedule_amount() self.validate_payment_schedule_amount()
def validate_all_documents_schedule(self): def validate_all_documents_schedule(self):
@ -233,9 +232,10 @@ class AccountsController(TransactionBase):
tax_master_doctype = self.meta.get_field("taxes_and_charges").options tax_master_doctype = self.meta.get_field("taxes_and_charges").options
if self.is_new() and not self.get("taxes"): if self.is_new() and not self.get("taxes"):
if not self.get("taxes_and_charges"): if self.company and not self.get("taxes_and_charges"):
# get the default tax master # get the default tax master
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype, {"is_default": 1}) self.taxes_and_charges = frappe.db.get_value(tax_master_doctype,
{"is_default": 1, 'company': self.company})
self.append_taxes_from_master(tax_master_doctype) self.append_taxes_from_master(tax_master_doctype)
@ -375,6 +375,17 @@ class AccountsController(TransactionBase):
return res return res
def is_inclusive_tax(self):
is_inclusive = cint(frappe.db.get_single_value("Accounts Settings",
"show_inclusive_tax_in_print"))
if is_inclusive:
is_inclusive = 0
if self.get("taxes", filters={"included_in_print_rate": 1}):
is_inclusive = 1
return is_inclusive
def validate_advance_entries(self): def validate_advance_entries(self):
order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order" order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
order_list = list(set([d.get(order_field) order_list = list(set([d.get(order_field)
@ -650,6 +661,8 @@ class AccountsController(TransactionBase):
date = self.get("due_date") date = self.get("due_date")
due_date = date or posting_date due_date = date or posting_date
grand_total = self.get("rounded_total") or self.grand_total grand_total = self.get("rounded_total") or self.grand_total
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if not self.get("payment_schedule"): if not self.get("payment_schedule"):
if self.get("payment_terms_template"): if self.get("payment_terms_template"):
@ -661,7 +674,8 @@ class AccountsController(TransactionBase):
self.append("payment_schedule", data) self.append("payment_schedule", data)
else: else:
for d in self.get("payment_schedule"): for d in self.get("payment_schedule"):
d.payment_amount = grand_total * flt(d.invoice_portion) / 100 if d.invoice_portion:
d.payment_amount = grand_total * flt(d.invoice_portion) / 100
def set_due_date(self): def set_due_date(self):
due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date] due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date]
@ -671,15 +685,12 @@ class AccountsController(TransactionBase):
def validate_payment_schedule_dates(self): def validate_payment_schedule_dates(self):
dates = [] dates = []
li = [] li = []
if self.due_date and getdate(self.due_date) < getdate(self.posting_date):
frappe.throw(_("Due Date cannot be before posting date"))
for d in self.get("payment_schedule"): for d in self.get("payment_schedule"):
if getdate(d.due_date) < getdate(self.posting_date): if self.doctype == "Sales Order" and getdate(d.due_date) < getdate(self.transaction_date):
frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx)) frappe.throw(_("Row {0}: Due Date cannot be before posting date").format(d.idx))
elif d.due_date in dates: elif d.due_date in dates:
li.append('{0} in row {1}'.format(d.due_date, d.idx)) li.append('{0} in row {1}'.format(d.due_date, d.idx))
# frappe.throw(_("Row {0}: Duplicate due date found").format(d.idx))
dates.append(d.due_date) dates.append(d.due_date)
if li: if li:
@ -692,20 +703,14 @@ class AccountsController(TransactionBase):
total = 0 total = 0
for d in self.get("payment_schedule"): for d in self.get("payment_schedule"):
total += flt(d.payment_amount) total += flt(d.payment_amount)
total = flt(total, self.precision("grand_total"))
grand_total = self.get("rounded_total") or self.grand_total grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if total != grand_total: if total != grand_total:
frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total")) frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
def validate_invoice_portion(self):
if self.get("payment_schedule"):
total_portion = 0
for term in self.payment_schedule:
total_portion += flt(term.get('invoice_portion', 0))
if flt(total_portion, 2) != 100.00:
frappe.throw(_('Combined invoice portion must equal 100%'), indicator='red')
def is_rounded_total_disabled(self): def is_rounded_total_disabled(self):
if self.meta.get_field("disable_rounded_total"): if self.meta.get_field("disable_rounded_total"):
return self.disable_rounded_total return self.disable_rounded_total
@ -717,8 +722,12 @@ def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
@frappe.whitelist() @frappe.whitelist()
def get_default_taxes_and_charges(master_doctype): def get_default_taxes_and_charges(master_doctype, company=None):
default_tax = frappe.db.get_value(master_doctype, {"is_default": 1}) if not company: return {}
default_tax = frappe.db.get_value(master_doctype,
{"is_default": 1, "company": company})
return { return {
'taxes_and_charges': default_tax, 'taxes_and_charges': default_tax,
'taxes': get_taxes_and_charges(master_doctype, default_tax) 'taxes': get_taxes_and_charges(master_doctype, default_tax)

View File

@ -8,7 +8,7 @@ from frappe.utils import flt,cint, cstr, getdate
from erpnext.accounts.party import get_party_details from erpnext.accounts.party import get_party_details
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from erpnext.buying.utils import validate_for_items from erpnext.buying.utils import validate_for_items, update_last_purchase_rate
from erpnext.stock.stock_ledger import get_valuation_rate from erpnext.stock.stock_ledger import get_valuation_rate
from erpnext.controllers.stock_controller import StockController from erpnext.controllers.stock_controller import StockController
@ -18,7 +18,10 @@ class BuyingController(StockController):
if hasattr(self, "taxes"): if hasattr(self, "taxes"):
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings", self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
"print_taxes_with_zero_amount")) "print_taxes_with_zero_amount"))
self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax()
self.print_templates = { self.print_templates = {
"total": "templates/print_formats/includes/total.html",
"taxes": "templates/print_formats/includes/taxes.html" "taxes": "templates/print_formats/includes/taxes.html"
} }
@ -160,6 +163,11 @@ class BuyingController(StockController):
if item in self.sub_contracted_items and not item.bom: if item in self.sub_contracted_items and not item.bom:
frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code)) frappe.throw(_("Please select BOM in BOM field for Item {0}").format(item.item_code))
if self.doctype == "Purchase Order":
for supplied_item in self.get("supplied_items"):
if not supplied_item.reserve_warehouse:
frappe.throw(_("Reserved Warehouse is mandatory for Item {0} in Raw Materials supplied").format(frappe.bold(supplied_item.rm_item_code)))
else: else:
for item in self.get("items"): for item in self.get("items"):
if item.bom: if item.bom:
@ -189,8 +197,16 @@ class BuyingController(StockController):
def update_raw_materials_supplied(self, item, raw_material_table): def update_raw_materials_supplied(self, item, raw_material_table):
bom_items = self.get_items_from_bom(item.item_code, item.bom) bom_items = self.get_items_from_bom(item.item_code, item.bom)
raw_materials_cost = 0 raw_materials_cost = 0
items = list(set([d.item_code for d in bom_items]))
item_wh = frappe._dict(frappe.db.sql("""select item_code, default_warehouse
from `tabItem` where name in ({0})""".format(", ".join(["%s"] * len(items))), items))
for bom_item in bom_items: for bom_item in bom_items:
if self.doctype == "Purchase Order":
reserve_warehouse = bom_item.source_warehouse or item_wh.get(bom_item.item_code)
if frappe.db.get_value("Warehouse", reserve_warehouse, "company") != self.company:
reserve_warehouse = None
# check if exists # check if exists
exists = 0 exists = 0
for d in self.get(raw_material_table): for d in self.get(raw_material_table):
@ -210,6 +226,8 @@ class BuyingController(StockController):
rm.rm_item_code = bom_item.item_code rm.rm_item_code = bom_item.item_code
rm.stock_uom = bom_item.stock_uom rm.stock_uom = bom_item.stock_uom
rm.required_qty = required_qty rm.required_qty = required_qty
if self.doctype == "Purchase Order" and not rm.reserve_warehouse:
rm.reserve_warehouse = reserve_warehouse
rm.conversion_factor = item.conversion_factor rm.conversion_factor = item.conversion_factor
@ -261,7 +279,7 @@ class BuyingController(StockController):
def get_items_from_bom(self, item_code, bom): def get_items_from_bom(self, item_code, bom):
bom_items = frappe.db.sql("""select t2.item_code, bom_items = frappe.db.sql("""select t2.item_code,
t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit, t2.stock_qty / ifnull(t1.quantity, 1) as qty_consumed_per_unit,
t2.rate, t2.stock_uom, t2.name, t2.description t2.rate, t2.stock_uom, t2.name, t2.description, t2.source_warehouse
from `tabBOM` t1, `tabBOM Item` t2, tabItem t3 from `tabBOM` t1, `tabBOM Item` t2, tabItem t3
where t2.parent = t1.name and t1.item = %s where t2.parent = t1.name and t1.item = %s
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
@ -336,7 +354,7 @@ class BuyingController(StockController):
frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code']))) frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])))
def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False): def update_stock_ledger(self, allow_negative_stock=False, via_landed_cost_voucher=False):
self.update_ordered_qty() self.update_ordered_and_reserved_qty()
sl_entries = [] sl_entries = []
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
@ -378,7 +396,7 @@ class BuyingController(StockController):
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock, self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher) via_landed_cost_voucher=via_landed_cost_voucher)
def update_ordered_qty(self): def update_ordered_and_reserved_qty(self):
po_map = {} po_map = {}
for d in self.get("items"): for d in self.get("items"):
if self.doctype=="Purchase Receipt" \ if self.doctype=="Purchase Receipt" \
@ -397,6 +415,8 @@ class BuyingController(StockController):
frappe.InvalidStatusError) frappe.InvalidStatusError)
po_obj.update_ordered_qty(po_item_rows) po_obj.update_ordered_qty(po_item_rows)
if self.is_subcontracted:
po_obj.update_reserved_qty_for_subcontract()
def make_sl_entries_for_supplier_warehouse(self, sl_entries): def make_sl_entries_for_supplier_warehouse(self, sl_entries):
if hasattr(self, 'supplied_items'): if hasattr(self, 'supplied_items'):
@ -409,6 +429,18 @@ class BuyingController(StockController):
"actual_qty": -1*flt(d.consumed_qty), "actual_qty": -1*flt(d.consumed_qty),
})) }))
def on_submit(self):
if self.get('is_return'):
return
update_last_purchase_rate(self, is_submit = 1)
def on_cancel(self):
if self.get('is_return'):
return
update_last_purchase_rate(self, is_submit = 0)
def validate_schedule_date(self): def validate_schedule_date(self):
if not self.schedule_date: if not self.schedule_date:
self.schedule_date = min([d.schedule_date for d in self.get("items")]) self.schedule_date = min([d.schedule_date for d in self.get("items")])
@ -418,8 +450,9 @@ class BuyingController(StockController):
if not d.schedule_date: if not d.schedule_date:
d.schedule_date = self.schedule_date d.schedule_date = self.schedule_date
if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date): if (d.schedule_date and self.transaction_date and
frappe.throw(_("Expected Date cannot be before Transaction Date")) getdate(d.schedule_date) < getdate(self.transaction_date)):
frappe.throw(_("Row #{0}: Reqd by Date cannot be before Transaction Date").format(d.idx))
else: else:
frappe.throw(_("Please enter Schedule Date")) frappe.throw(_("Please enter Reqd by Date"))

View File

@ -56,7 +56,7 @@ def validate_item_variant_attributes(item, args=None):
if not args: if not args:
args = {d.attribute.lower():d.attribute_value for d in item.attributes} args = {d.attribute.lower():d.attribute_value for d in item.attributes}
attribute_values, numeric_values = get_attribute_values() attribute_values, numeric_values = get_attribute_values(item)
for attribute, value in args.items(): for attribute, value in args.items():
if not value: if not value:
@ -96,16 +96,17 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format( frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values for Item {2}").format(
attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute')) attribute_value, attribute, item), InvalidItemAttributeValueError, title=_('Invalid Attribute'))
def get_attribute_values(): def get_attribute_values(item):
if not frappe.flags.attribute_values: if not frappe.flags.attribute_values:
attribute_values = {} attribute_values = {}
numeric_values = {} numeric_values = {}
for t in frappe.get_all("Item Attribute Value", fields=["parent", "attribute_value"]): for t in frappe.get_all("Item Attribute Value", fields=["parent", "attribute_value"]):
attribute_values.setdefault(t.parent.lower(), []).append(t.attribute_value) attribute_values.setdefault(t.parent.lower(), []).append(t.attribute_value)
for t in frappe.get_all('Item Attribute', for t in frappe.get_all('Item Variant Attribute',
fields=["name", "from_range", "to_range", "increment"], filters={'numeric_values': 1}): fields=["attribute", "from_range", "to_range", "increment"],
numeric_values[t.name.lower()] = t filters={'numeric_values': 1, 'parent': item.variant_of}):
numeric_values[t.attribute.lower()] = t
frappe.flags.attribute_values = attribute_values frappe.flags.attribute_values = attribute_values
frappe.flags.numeric_values = numeric_values frappe.flags.numeric_values = numeric_values

View File

@ -418,6 +418,6 @@ def get_batch_numbers(doctype, txt, searchfield, start, page_len, filters):
'where (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL)' 'where (`tabBatch`.expiry_date >= CURDATE() or `tabBatch`.expiry_date IS NULL)'
if filters and filters.get('item_code'): if filters and filters.get('item_code'):
query += 'where item = %(item_code)s' % filters query += 'and item = %(item_code)s'
return frappe.db.sql(query) return frappe.db.sql(query, filters)

View File

@ -16,7 +16,10 @@ class SellingController(StockController):
if hasattr(self, "taxes"): if hasattr(self, "taxes"):
self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings", self.flags.print_taxes_with_zero_amount = cint(frappe.db.get_single_value("Print Settings",
"print_taxes_with_zero_amount")) "print_taxes_with_zero_amount"))
self.flags.show_inclusive_tax_in_print = self.is_inclusive_tax()
self.print_templates = { self.print_templates = {
"total": "templates/print_formats/includes/total.html",
"taxes": "templates/print_formats/includes/taxes.html" "taxes": "templates/print_formats/includes/taxes.html"
} }
@ -185,7 +188,10 @@ class SellingController(StockController):
'batch_no': cstr(p.batch_no).strip(), 'batch_no': cstr(p.batch_no).strip(),
'serial_no': cstr(p.serial_no).strip(), 'serial_no': cstr(p.serial_no).strip(),
'name': d.name, 'name': d.name,
'target_warehouse': p.target_warehouse 'target_warehouse': p.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
})) }))
else: else:
il.append(frappe._dict({ il.append(frappe._dict({
@ -198,7 +204,10 @@ class SellingController(StockController):
'batch_no': cstr(d.get("batch_no")).strip(), 'batch_no': cstr(d.get("batch_no")).strip(),
'serial_no': cstr(d.get("serial_no")).strip(), 'serial_no': cstr(d.get("serial_no")).strip(),
'name': d.name, 'name': d.name,
'target_warehouse': d.target_warehouse 'target_warehouse': d.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
'allow_zero_valuation': d.allow_zero_valuation_rate
})) }))
return il return il
@ -293,7 +302,11 @@ class SellingController(StockController):
"posting_date": self.posting_date, "posting_date": self.posting_date,
"posting_time": self.posting_time, "posting_time": self.posting_time,
"qty": -1*flt(d.qty), "qty": -1*flt(d.qty),
"serial_no": d.serial_no "serial_no": d.serial_no,
"company": d.company,
"voucher_type": d.voucher_type,
"voucher_no": d.name,
"allow_zero_valuation": d.allow_zero_valuation
}) })
target_warehouse_sle.update({ target_warehouse_sle.update({
"incoming_rate": get_incoming_rate(args) "incoming_rate": get_incoming_rate(args)

View File

@ -70,7 +70,6 @@ class calculate_taxes_and_totals(object):
item.net_rate = item.rate item.net_rate = item.rate
item.amount = flt(item.rate * item.qty, item.precision("amount")) item.amount = flt(item.rate * item.qty, item.precision("amount"))
item.net_amount = item.amount item.net_amount = item.amount
item.total_weight = flt(item.weight_per_unit * item.qty)
self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"]) self._set_in_company_currency(item, ["price_list_rate", "rate", "net_rate", "amount", "net_amount"])
@ -164,13 +163,12 @@ class calculate_taxes_and_totals(object):
return tax.rate return tax.rate
def calculate_net_total(self): def calculate_net_total(self):
self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = self.doc.total_net_weight= 0.0 self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0
for item in self.doc.get("items"): for item in self.doc.get("items"):
self.doc.total += item.amount self.doc.total += item.amount
self.doc.base_total += item.base_amount self.doc.base_total += item.base_amount
self.doc.net_total += item.net_amount self.doc.net_total += item.net_amount
self.doc.base_net_total += item.base_net_amount self.doc.base_net_total += item.base_net_amount
self.doc.total_net_weight += item.total_weight
self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"]) self.doc.round_floats_in(self.doc, ["total", "base_total", "net_total", "base_net_total"])
@ -400,7 +398,8 @@ class calculate_taxes_and_totals(object):
for tax in self.doc.get("taxes"): for tax in self.doc.get("taxes"):
if tax.charge_type == "Actual": if tax.charge_type == "Actual":
actual_taxes_dict.setdefault(tax.idx, tax.tax_amount) tax_amount = self.get_tax_amount_if_for_valuation_or_deduction(tax.tax_amount, tax)
actual_taxes_dict.setdefault(tax.idx, tax_amount)
elif tax.row_id in actual_taxes_dict: elif tax.row_id in actual_taxes_dict:
actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100 actual_tax_amount = flt(actual_taxes_dict.get(tax.row_id, 0)) * flt(tax.rate) / 100
actual_taxes_dict.setdefault(tax.idx, actual_tax_amount) actual_taxes_dict.setdefault(tax.idx, actual_tax_amount)
@ -535,7 +534,7 @@ def get_itemised_tax_breakup_html(doc):
for tax in doc.taxes: for tax in doc.taxes:
if getattr(tax, "category", None) and tax.category=="Valuation": if getattr(tax, "category", None) and tax.category=="Valuation":
continue continue
if tax.description not in tax_accounts and tax.tax_amount_after_discount_amount: if tax.description not in tax_accounts:
tax_accounts.append(tax.description) tax_accounts.append(tax.description)
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts) headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
@ -545,6 +544,7 @@ def get_itemised_tax_breakup_html(doc):
get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes")) get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
update_itemised_tax_data(doc)
frappe.flags.company = None frappe.flags.company = None
return frappe.render_template( return frappe.render_template(
@ -557,6 +557,12 @@ def get_itemised_tax_breakup_html(doc):
) )
) )
@erpnext.allow_regional
def update_itemised_tax_data(doc):
#Don't delete this method, used for localization
pass
@erpnext.allow_regional @erpnext.allow_regional
def get_itemised_tax_breakup_header(item_doctype, tax_accounts): def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
return [_("Item"), _("Taxable Amount")] + tax_accounts return [_("Item"), _("Taxable Amount")] + tax_accounts

View File

@ -101,7 +101,7 @@ class Lead(SellingController):
def set_lead_name(self): def set_lead_name(self):
if not self.lead_name: if not self.lead_name:
frappe.db.set_value("Lead", self.name, "lead_name", self.organization_name) frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
@frappe.whitelist() @frappe.whitelist()
def make_customer(source_name, target_doc=None): def make_customer(source_name, target_doc=None):
@ -129,7 +129,6 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False):
} }
}}, target_doc, set_missing_values, ignore_permissions=ignore_permissions) }}, target_doc, set_missing_values, ignore_permissions=ignore_permissions)
print(doclist)
return doclist return doclist
@frappe.whitelist() @frappe.whitelist()

View File

@ -29,7 +29,7 @@
"email_id": "test_lead4@example.com", "email_id": "test_lead4@example.com",
"organization_lead": 1, "organization_lead": 1,
"lead_name": "_Test Lead 4", "lead_name": "_Test Lead 4",
"organization_name": "_Test Lead 4", "company_name": "_Test Lead 4",
"status": "Open" "status": "Open"
} }
] ]

View File

@ -232,7 +232,7 @@ def make_quotation(source_name, target_doc=None):
quotation.conversion_rate = exchange_rate quotation.conversion_rate = exchange_rate
# get default taxes # get default taxes
taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template") taxes = get_default_taxes_and_charges("Sales Taxes and Charges Template", quotation.company)
if taxes.get('taxes'): if taxes.get('taxes'):
quotation.update(taxes) quotation.update(taxes)

View File

@ -2,36 +2,43 @@
{ {
"asset_name": "Macbook Pro - 1", "asset_name": "Macbook Pro - 1",
"item_code": "Computer", "item_code": "Computer",
"gross_purchase_amount": 100000 "gross_purchase_amount": 100000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "Macbook Air - 1", "asset_name": "Macbook Air - 1",
"item_code": "Computer", "item_code": "Computer",
"gross_purchase_amount": 60000 "gross_purchase_amount": 60000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "Conferrence Table", "asset_name": "Conferrence Table",
"item_code": "Table", "item_code": "Table",
"gross_purchase_amount": 30000 "gross_purchase_amount": 30000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "Lunch Table", "asset_name": "Lunch Table",
"item_code": "Table", "item_code": "Table",
"gross_purchase_amount": 20000 "gross_purchase_amount": 20000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "ERPNext", "asset_name": "ERPNext",
"item_code": "ERP", "item_code": "ERP",
"gross_purchase_amount": 100000 "gross_purchase_amount": 100000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "Chair 1", "asset_name": "Chair 1",
"item_code": "Chair", "item_code": "Chair",
"gross_purchase_amount": 10000 "gross_purchase_amount": 10000,
"asset_owner": "Company"
}, },
{ {
"asset_name": "Chair 2", "asset_name": "Chair 2",
"item_code": "Chair", "item_code": "Chair",
"gross_purchase_amount": 10000 "gross_purchase_amount": 10000,
"asset_owner": "Company"
} }
] ]

View File

@ -55,7 +55,7 @@ def complete_setup(domain='Manufacturing'):
"fy_start_date": "2015-01-01", "fy_start_date": "2015-01-01",
"fy_end_date": "2015-12-31", "fy_end_date": "2015-12-31",
"bank_account": "National Bank", "bank_account": "National Bank",
"domain": domain, "domains": [domain],
"company_name": data.get(domain).get('company_name'), "company_name": data.get(domain).get('company_name'),
"chart_of_accounts": "Standard", "chart_of_accounts": "Standard",
"company_abbr": ''.join([d[0] for d in data.get(domain).get('company_name').split()]).upper(), "company_abbr": ''.join([d[0] for d in data.get(domain).get('company_name').split()]).upper(),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Some files were not shown because too many files have changed in this diff Show More