Merge branch 'develop' into show_address_in_online_pos

This commit is contained in:
saurabh-bhosale 2018-01-30 17:48:12 +05:30 committed by GitHub
commit 0c9a99a953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 917 additions and 362 deletions

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__ = '10.0.14' __version__ = '10.0.17'
def get_default_company(user=None): def get_default_company(user=None):
'''Get default company for user''' '''Get default company for user'''

View File

@ -82,8 +82,8 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
$.each(this.frm.doc.accounts || [], function(i, jvd) { $.each(this.frm.doc.accounts || [], function(i, jvd) {
frappe.model.set_default_values(jvd); frappe.model.set_default_values(jvd);
}); });
var posting_date = this.frm.posting_date;
if(!this.frm.doc.amended_from) this.frm.doc.posting_date = this.frm.posting_date || frappe.datetime.get_today(); if(!this.frm.doc.amended_from) this.frm.set_value('posting_date', posting_date || frappe.datetime.get_today());
} }
}, },

View File

@ -96,7 +96,7 @@ class PurchaseInvoice(BuyingController):
if not self.credit_to: if not self.credit_to:
self.credit_to = get_party_account("Supplier", self.supplier, self.company) self.credit_to = get_party_account("Supplier", self.supplier, self.company)
if not self.due_date: if not self.due_date:
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier) self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
super(PurchaseInvoice, self).set_missing_values(for_validate) super(PurchaseInvoice, self).set_missing_values(for_validate)

View File

@ -237,7 +237,7 @@ class SalesInvoice(SellingController):
if not self.debit_to: if not self.debit_to:
self.debit_to = get_party_account("Customer", self.customer, self.company) self.debit_to = get_party_account("Customer", self.customer, self.company)
if not self.due_date and self.customer: if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer) self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
super(SalesInvoice, self).set_missing_values(for_validate) super(SalesInvoice, self).set_missing_values(for_validate)

View File

@ -232,6 +232,8 @@ def get_next_date(dt, mcount, day=None):
def send_notification(new_rv, subscription_doc, print_format='Standard'): def send_notification(new_rv, subscription_doc, print_format='Standard'):
"""Notify concerned persons about recurring document generation""" """Notify concerned persons about recurring document generation"""
print_format = print_format print_format = print_format
subject = subscription_doc.subject or ''
message = subscription_doc.message or ''
if not subscription_doc.subject: if not subscription_doc.subject:
subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name) subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name)

View File

@ -191,8 +191,9 @@ def delete_gl_entries(gl_entries=None, voucher_type=None, voucher_no=None,
for entry in gl_entries: for entry in gl_entries:
validate_frozen_account(entry["account"], adv_adj) validate_frozen_account(entry["account"], adv_adj)
validate_balance_type(entry["account"], adv_adj) validate_balance_type(entry["account"], adv_adj)
if not adv_adj:
validate_expense_against_budget(entry) validate_expense_against_budget(entry)
if entry.get("against_voucher") and update_outstanding == 'Yes': if entry.get("against_voucher") and update_outstanding == 'Yes' and not adv_adj:
update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"), update_outstanding_amt(entry["account"], entry.get("party_type"), entry.get("party"), entry.get("against_voucher_type"),
entry.get("against_voucher"), on_cancel=True) entry.get("against_voucher"), on_cancel=True)

View File

@ -51,7 +51,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
set_other_values(out, party, party_type) set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list) set_price_list(out, party, party_type, price_list)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type) out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type) out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
if not out.get("currency"): if not out.get("currency"):
out["currency"] = currency out["currency"] = currency
@ -164,7 +164,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
out = { out = {
party_type.lower(): party, party_type.lower(): party,
account_fieldname : account, account_fieldname : account,
"due_date": get_due_date(posting_date, party_type, party) "due_date": get_due_date(posting_date, party_type, party, company)
} }
return out return out
@ -267,12 +267,12 @@ def validate_party_accounts(doc):
@frappe.whitelist() @frappe.whitelist()
def get_due_date(posting_date, party_type, party): def get_due_date(posting_date, party_type, party, company=None):
"""Get due date from `Payment Terms Template`""" """Get due date from `Payment Terms Template`"""
due_date = None due_date = None
if posting_date and party: if posting_date and party:
due_date = posting_date due_date = posting_date
template_name = get_pyt_term_template(party, party_type) template_name = get_pyt_term_template(party, party_type, company)
if template_name: if template_name:
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d") due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
else: else:
@ -305,12 +305,11 @@ def get_due_date_from_template(template_name, posting_date):
return due_date return due_date
def validate_due_date(posting_date, due_date, party_type, party, company=None):
def validate_due_date(posting_date, due_date, party_type, party):
if getdate(due_date) < getdate(posting_date): if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting Date")) frappe.throw(_("Due Date cannot be before Posting Date"))
else: else:
default_due_date = get_due_date(posting_date, party_type, party) default_due_date = get_due_date(posting_date, party_type, party, company)
if not default_due_date: if not default_due_date:
return return
@ -360,14 +359,32 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
@frappe.whitelist() @frappe.whitelist()
def get_pyt_term_template(party_name, party_type): def get_pyt_term_template(party_name, party_type, company=None):
if party_type not in ("Customer", "Supplier"):
return
template = None template = None
if party_type in ('Customer', 'Supplier'): if party_type == 'Customer':
template = frappe.db.get_value(party_type, party_name, fieldname='payment_terms') customer = frappe.db.get_value("Customer", party_name,
fieldname=['payment_terms', "customer_group"], as_dict=1)
template = customer.payment_terms
if not template and customer.customer_group:
template = frappe.db.get_value("Customer Group",
customer.customer_group, fieldname='payment_terms')
else:
supplier = frappe.db.get_value("Supplier", party_name,
fieldname=['payment_terms', "supplier_type"], as_dict=1)
template = supplier.payment_terms
if not template and supplier.supplier_type:
template = frappe.db.get_value("Supplier Type", supplier.supplier_type, fieldname='payment_terms')
if not template and company:
template = frappe.db.get_value("Company", company, fieldname='payment_terms')
return template return template
def validate_party_frozen_disabled(party_type, party_name): def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name: if party_type and party_name:
if party_type in ("Customer", "Supplier"): if party_type in ("Customer", "Supplier"):

View File

@ -28,6 +28,24 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Link", "fieldtype": "Link",
"options": "Payment Terms Template" "options": "Payment Terms Template"
}, },
{
"fieldname":"territory",
"label": __("Territory"),
"fieldtype": "Link",
"options": "Territory"
},
{
"fieldname":"sales_partner",
"label": __("Sales Partner"),
"fieldtype": "Link",
"options": "Sales Partner"
},
{
"fieldname":"sales_person",
"label": __("Sales Person"),
"fieldtype": "Link",
"options": "Sales Person"
},
{ {
"fieldtype": "Break", "fieldtype": "Break",
}, },

View File

@ -283,10 +283,26 @@ class ReceivablePayableReport(object):
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1} where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
and name=tabCustomer.customer_group))""".format(lft, rgt)) and name=tabCustomer.customer_group))""".format(lft, rgt))
if self.filters.get("territory"):
lft, rgt = frappe.db.get_value("Territory",
self.filters.get("territory"), ["lft", "rgt"])
conditions.append("""party in (select name from tabCustomer
where exists(select name from `tabTerritory` where lft >= {0} and rgt <= {1}
and name=tabCustomer.territory))""".format(lft, rgt))
if self.filters.get("payment_terms_template"): if self.filters.get("payment_terms_template"):
conditions.append("party in (select name from tabCustomer where payment_terms=%s)") conditions.append("party in (select name from tabCustomer where payment_terms=%s)")
values.append(self.filters.get("payment_terms_template")) values.append(self.filters.get("payment_terms_template"))
if self.filters.get("sales_partner"):
conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)")
values.append(self.filters.get("sales_partner"))
if self.filters.get("sales_person"):
conditions.append("""party in (select parent
from `tabSales Team` where sales_person=%s and parenttype = 'Customer')""")
values.append(self.filters.get("sales_person"))
return " and ".join(conditions), values return " and ".join(conditions), values
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher): def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):

View File

@ -9,7 +9,6 @@
"doctype": "Supplier", "doctype": "Supplier",
"supplier_name": "_Test Supplier P", "supplier_name": "_Test Supplier P",
"supplier_type": "_Test Supplier Type", "supplier_type": "_Test Supplier Type",
"credit_days_based_on": "Fixed Days"
}, },
{ {
"doctype": "Supplier", "doctype": "Supplier",

View File

@ -291,30 +291,15 @@ def get_data():
"label": _("Healthcare"), "label": _("Healthcare"),
"hidden": 1 "hidden": 1
}, },
{
"module_name": "Lab Test",
"color": "#7578f6",
"icon": "octicon octicon-beaker",
"doctype": "Lab Test",
"type": "list",
"link": "List/Lab Test",
"label": _("Lab Test")
},
{
"module_name": "Consultation",
"color": "#2ecc71",
"icon": "fa fa-stethoscope",
"doctype": "Consultation",
"type": "link",
"label": _("Consultationt")
},
{ {
"module_name": "Patient", "module_name": "Patient",
"color": "#6BE273", "color": "#6BE273",
"icon": "fa fa-user", "icon": "fa fa-user",
"doctype": "Patient", "doctype": "Patient",
"type": "link", "type": "link",
"label": _("Patient") "link": "List/Patient",
"label": _("Patient"),
"hidden": 1
}, },
{ {
"module_name": "Patient Appointment", "module_name": "Patient Appointment",
@ -322,7 +307,29 @@ def get_data():
"icon": "fa fa-calendar-plus-o", "icon": "fa fa-calendar-plus-o",
"doctype": "Patient Appointment", "doctype": "Patient Appointment",
"type": "link", "type": "link",
"label": _("Patient Appointment") "link": "List/Patient Appointment",
"label": _("Patient Appointment"),
"hidden": 1
},
{
"module_name": "Consultation",
"color": "#2ecc71",
"icon": "fa fa-stethoscope",
"doctype": "Consultation",
"type": "link",
"link": "List/Consultation",
"label": _("Consultation"),
"hidden": 1
},
{
"module_name": "Lab Test",
"color": "#7578f6",
"icon": "octicon octicon-beaker",
"doctype": "Lab Test",
"type": "list",
"link": "List/Lab Test",
"label": _("Lab Test"),
"hidden": 1
}, },
{ {
"module_name": "Hub", "module_name": "Hub",

View File

@ -135,9 +135,9 @@ class AccountsController(TransactionBase):
if not self.due_date: if not self.due_date:
frappe.throw(_("Due Date is mandatory")) frappe.throw(_("Due Date is mandatory"))
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer) validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
elif self.doctype == "Purchase Invoice": elif self.doctype == "Purchase Invoice":
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier) validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
def set_price_list_currency(self, buying_or_selling): def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("posting_date"): if self.meta.get_field("posting_date"):

View File

@ -0,0 +1 @@
fichier_des_ecritures_comptables

View File

@ -269,7 +269,10 @@ def get_grade(grading_scale, percentage):
:param Percentage: Score Percentage Percentage :param Percentage: Score Percentage Percentage
""" """
grading_scale_intervals = {} grading_scale_intervals = {}
for d in frappe.get_all("Grading Scale Interval", fields=["grade_code", "threshold"], filters={"parent": grading_scale}): if not hasattr(frappe.local, 'grading_scale'):
grading_scale = frappe.get_all("Grading Scale Interval", fields=["grade_code", "threshold"], filters={"parent": grading_scale})
frappe.local.grading_scale = grading_scale
for d in frappe.local.grading_scale:
grading_scale_intervals.update({d.threshold:d.grade_code}) grading_scale_intervals.update({d.threshold:d.grade_code})
intervals = sorted(grading_scale_intervals.keys(), key=float, reverse=True) intervals = sorted(grading_scale_intervals.keys(), key=float, reverse=True)
for interval in intervals: for interval in intervals:

View File

@ -6,5 +6,9 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
STD_CRITERIA = ["total", "total score", "total grade", "maximum score", "score", "grade"]
class AssessmentCriteria(Document): class AssessmentCriteria(Document):
pass def validate(self):
if self.assessment_criteria.lower() in STD_CRITERIA:
frappe.throw("Can't create standard criteria. Please rename the criteria")

View File

@ -6,9 +6,15 @@
{% } %} {% } %}
<h4 class="text-center">{%= __("Assessment Report") %}</h4> <h4 class="text-center">{%= __("Assessment Report") %}</h4>
<hr> <hr>
<h5 class="text-center">{%= __("Academic Year: ") %} {%= filters.academic_year %} </h5>
{% if (filters.academic_term){ %}
<h5 class="text-center">{%= __("Academic Term: ") %} {%= filters.academic_term %} </h5>
{% } %}
<h5 class="text-center">{%= __("Course Code: ") %} {%= filters.course %}</h5> <h5 class="text-center">{%= __("Course Code: ") %} {%= filters.course %}</h5>
<h5 class="text-center">{%= __("Assessment Group: ") %} {%= filters.assessment_group %}</h5> <h5 class="text-center">{%= __("Assessment Group: ") %} {%= filters.assessment_group %}</h5>
<h5 class="text-center">{%= __("Assessment Plan: ") %} {%= data_to_be_printed[0]["assessment_plan"] %} </h5> {% if (filters.student_group){ %}
<h5 class="text-center">{%= __("Student Group: ") %} {%= filters.student_group %} </h5>
{% } %}
<hr> <hr>
<table class="table table-bordered"> <table class="table table-bordered">

View File

@ -4,18 +4,17 @@
frappe.query_reports["Course wise Assessment Report"] = { frappe.query_reports["Course wise Assessment Report"] = {
"filters": [ "filters": [
{ {
"fieldname":"assessment_group", "fieldname":"academic_year",
"label": __("Assessment Group"), "label": __("Academic Year"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Assessment Group", "options": "Academic Year",
"reqd": 1, "reqd": 1
"get_query": function() { },
return{ {
filters: { "fieldname":"academic_term",
'is_group': 0 "label": __("Academic Term"),
} "fieldtype": "Link",
}; "options": "Academic Term"
}
}, },
{ {
"fieldname":"course", "fieldname":"course",
@ -29,6 +28,13 @@ frappe.query_reports["Course wise Assessment Report"] = {
"label": __("Student Group"), "label": __("Student Group"),
"fieldtype": "Link", "fieldtype": "Link",
"options": "Student Group" "options": "Student Group"
},
{
"fieldname":"assessment_group",
"label": __("Assessment Group"),
"fieldtype": "Link",
"options": "Assessment Group",
"reqd": 1
} }
] ]
}; };

View File

@ -5,129 +5,189 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt from frappe.utils import flt
from collections import defaultdict from collections import defaultdict, OrderedDict
from erpnext.education.api import get_grade from erpnext.education.api import get_grade
def execute(filters=None): def execute(filters=None):
data = [] data, chart, grades = [], [], []
args = frappe._dict() args = frappe._dict()
grade_wise_analysis = defaultdict(dict)
args["academic_year"] = filters.get("academic_year")
args["course"] = filters.get("course")
args["assessment_group"] = filters.get("assessment_group") args["assessment_group"] = filters.get("assessment_group")
args["academic_term"] = filters.get("academic_term")
args["student_group"] = filters.get("student_group")
if args["assessment_group"] == "All Assessment Groups": if args["assessment_group"] == "All Assessment Groups":
frappe.throw(_("Please select the assessment group other than 'All Assessment Groups'")) frappe.throw(_("Please select the assessment group other than 'All Assessment Groups'"))
args["course"] = filters.get("course") returned_values = get_formatted_result(args, get_assessment_criteria=True)
args["student_group"] = filters.get("student_group") student_dict = returned_values["student_details"]
result_dict = returned_values["assessment_result"]
assessment_criteria_dict = returned_values["assessment_criteria"]
for student in result_dict:
student_row = {}
student_row["student"] = student
student_row["student_name"] = student_dict[student]
for criteria in assessment_criteria_dict:
scrub_criteria = frappe.scrub(criteria)
if criteria in result_dict[student][args.course][args.assessment_group]:
student_row[scrub_criteria] = result_dict[student][args.course][args.assessment_group][criteria]["grade"]
student_row[scrub_criteria + "_score"] = result_dict[student][args.course][args.assessment_group][criteria]["score"]
# find all assessment plan and related details linked with the given filters # create the list of possible grades
def get_assessment_details(): if student_row[scrub_criteria] not in grades:
if args["student_group"]: grades.append(student_row[scrub_criteria])
cond = "and ap.student_group=%(student_group)s"
# create the dict of for gradewise analysis
if student_row[scrub_criteria] not in grade_wise_analysis[criteria]:
grade_wise_analysis[criteria][student_row[scrub_criteria]] = 1
else: else:
cond = '' grade_wise_analysis[criteria][student_row[scrub_criteria]] += 1
else:
student_row[frappe.scrub(criteria)] = ""
student_row[frappe.scrub(criteria)+ "_score"] = ""
data.append(student_row)
assessment_plan = frappe.db.sql(''' assessment_criteria_list = [d for d in assessment_criteria_dict]
select columns = get_column(assessment_criteria_dict)
ap.name, ap.student_group, ap.grading_scale, apc.assessment_criteria, apc.maximum_score as max_score chart = get_chart_data(grades, assessment_criteria_list, grade_wise_analysis)
from
`tabAssessment Plan` ap, `tabAssessment Plan Criteria` apc
where
ap.assessment_group=%(assessment_group)s and ap.course=%(course)s and
ap.name=apc.parent and ap.docstatus=1 {0}
order by
apc.assessment_criteria'''.format(cond), (args), as_dict=1)
assessment_plan_list = list(set([d["name"] for d in assessment_plan])) return columns, data, None, chart
if not assessment_plan_list:
frappe.throw(_("No assessment plan linked with this assessment group"))
assessment_criteria_list = list(set([(d["assessment_criteria"],d["max_score"]) for d in assessment_plan]))
student_group_list = list(set([d["student_group"] for d in assessment_plan]))
total_maximum_score = flt(sum([flt(d[1]) for d in assessment_criteria_list]))
grading_scale = assessment_plan[0]["grading_scale"]
return assessment_plan_list, assessment_criteria_list, total_maximum_score, grading_scale, student_group_list
# get all the result and make a dict map student as the key and value as dict of result def get_formatted_result(args, get_assessment_criteria=False, get_course=False):
def get_result_map(): cond, cond1, cond2, cond3, cond4 = " ", " ", " ", " ", " "
result_dict = defaultdict(dict) args_list = [args.academic_year]
kounter = defaultdict(dict)
assessment_result = frappe.db.sql('''select ar.student, ard.assessment_criteria, ard.grade, ard.score if args.course:
from `tabAssessment Result` ar, `tabAssessment Result Detail` ard cond = " and ar.course=%s"
where ar.assessment_plan in (%s) and ar.name=ard.parent and ar.docstatus=1 args_list.append(args.course)
order by ard.assessment_criteria''' %', '.join(['%s']*len(assessment_plan_list)),
tuple(assessment_plan_list), as_dict=1) if args.academic_term:
cond1 = " and ar.academic_term=%s"
args_list.append(args.academic_term)
if args.student_group:
cond2 = " and ar.student_group=%s"
args_list.append(args.student_group)
create_total_dict = False
group_type = frappe.get_value("Assessment Group", args.assessment_group, "is_group")
if group_type:
from frappe.desk.treeview import get_children
assessment_groups = [d.get("value") for d in get_children("Assessment Group",
args.assessment_group) if d.get("value") and not d.get("expandable")]
cond3 = " and ar.assessment_group in (%s)"%(', '.join(['%s']*len(assessment_groups)))
else:
assessment_groups = [args.assessment_group]
cond3 = " and ar.assessment_group=%s"
args_list += assessment_groups
if args.students:
cond4 = " and ar.student in (%s)"%(', '.join(['%s']*len(args.students)))
args_list += args.students
assessment_result = frappe.db.sql('''
SELECT
ar.student, ar.student_name, ar.academic_year, ar.academic_term, ar.program, ar.course,
ar.assessment_plan, ar.grading_scale, ar.assessment_group, ar.student_group,
ard.assessment_criteria, ard.maximum_score, ard.grade, ard.score
FROM
`tabAssessment Result` ar, `tabAssessment Result Detail` ard
WHERE
ar.name=ard.parent and ar.docstatus=1 and ar.academic_year=%s {0} {1} {2} {3} {4}
ORDER BY
ard.assessment_criteria'''.format(cond, cond1, cond2, cond3, cond4),
tuple(args_list), as_dict=1)
# create the nested dictionary structure as given below:
# <variable_name>.<student_name>.<course>.<assessment_group>.<assessment_criteria>.<grade/score/max_score>
# "Total Score" -> assessment criteria used for totaling and args.assessment_group -> for totaling all the assesments
student_details = {}
formatted_assessment_result = defaultdict(dict)
assessment_criteria_dict = OrderedDict()
course_dict = OrderedDict()
total_maximum_score = None
if not (len(assessment_groups) == 1 and assessment_groups[0] == args.assessment_group):
create_total_dict = True
# add the score for a given score and recalculate the grades
def add_score_and_recalculate_grade(result, assessment_group, assessment_criteria):
formatted_assessment_result[result.student][result.course][assessment_group]\
[assessment_criteria]["maximum_score"] += result.maximum_score
formatted_assessment_result[result.student][result.course][assessment_group]\
[assessment_criteria]["score"] += result.score
tmp_grade = get_grade(result.grading_scale, ((formatted_assessment_result[result.student][result.course]
[assessment_group][assessment_criteria]["score"])/(formatted_assessment_result[result.student]
[result.course][assessment_group][assessment_criteria]["maximum_score"]))*100)
formatted_assessment_result[result.student][result.course][assessment_group]\
[assessment_criteria]["grade"] = tmp_grade
# create the assessment criteria "Total Score" with the sum of all the scores of the assessment criteria in a given assessment group
def add_total_score(result, assessment_group):
if "Total Score" not in formatted_assessment_result[result.student][result.course][assessment_group]:
formatted_assessment_result[result.student][result.course][assessment_group]["Total Score"] = frappe._dict({
"assessment_criteria": "Total Score", "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
else:
add_score_and_recalculate_grade(result, assessment_group, "Total Score")
for result in assessment_result: for result in assessment_result:
if "total_score" in result_dict[result.student]: if result.student not in student_details:
total_score = result_dict[result.student]["total_score"] + result.score student_details[result.student] = result.student_name
else:
total_score = result.score
total = get_grade(grading_scale, (total_score/total_maximum_score)*100)
if result.grade in kounter[result.assessment_criteria]: assessment_criteria_details = frappe._dict({"assessment_criteria": result.assessment_criteria,
kounter[result.assessment_criteria][result.grade] += 1 "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
else:
kounter[result.assessment_criteria].update({result.grade: 1})
if "Total" not in kounter: if not formatted_assessment_result[result.student]:
kounter["Total"] = {} formatted_assessment_result[result.student] = defaultdict(dict)
if not formatted_assessment_result[result.student][result.course]:
formatted_assessment_result[result.student][result.course] = defaultdict(dict)
if "total" in result_dict[result.student]: if not create_total_dict:
prev_grade = result_dict[result.student]["total"] formatted_assessment_result[result.student][result.course][result.assessment_group]\
prev_grade_count = kounter["Total"].get(prev_grade) - 1 [result.assessment_criteria] = assessment_criteria_details
kounter["Total"].update({prev_grade: prev_grade_count}) add_total_score(result, result.assessment_group)
latest_grade_count = kounter["Total"].get(total)+1 if kounter["Total"].get(total) else 1
kounter["Total"].update({total: latest_grade_count})
result_dict[result.student].update({ # create the total of all the assessment groups criteria-wise
frappe.scrub(result.assessment_criteria): result.grade, elif create_total_dict:
frappe.scrub(result.assessment_criteria)+"_score": result.score, if not formatted_assessment_result[result.student][result.course][args.assessment_group]:
"total_score": total_score, formatted_assessment_result[result.student][result.course][args.assessment_group] = defaultdict(dict)
"total": total formatted_assessment_result[result.student][result.course][args.assessment_group]\
}) [result.assessment_criteria] = assessment_criteria_details
elif result.assessment_criteria not in formatted_assessment_result[result.student][result.course][args.assessment_group]:
formatted_assessment_result[result.student][result.course][args.assessment_group]\
[result.assessment_criteria] = assessment_criteria_details
elif result.assessment_criteria in formatted_assessment_result[result.student][result.course][args.assessment_group]:
add_score_and_recalculate_grade(result, args.assessment_group, result.assessment_criteria)
return result_dict, kounter add_total_score(result, args.assessment_group)
# make data from the result dict total_maximum_score = formatted_assessment_result[result.student][result.course][args.assessment_group]\
def get_data(): ["Total Score"]["maximum_score"]
student_list = frappe.db.sql('''select sgs.student, sgs.student_name if get_assessment_criteria:
from `tabStudent Group` sg, `tabStudent Group Student` sgs assessment_criteria_dict[result.assessment_criteria] = formatted_assessment_result[result.student][result.course]\
where sg.name = sgs.parent and sg.name in (%s) [args.assessment_group][result.assessment_criteria]["maximum_score"]
order by sgs.group_roll_number asc''' %', '.join(['%s']*len(student_group_list)), if get_course:
tuple(student_group_list), as_dict=1) course_dict[result.course] = total_maximum_score
for student in student_list: if get_assessment_criteria and total_maximum_score:
student.update(result_dict[student.student]) assessment_criteria_dict["Total Score"] = total_maximum_score
return student_list
return {
"student_details": student_details,
"assessment_result": formatted_assessment_result,
"assessment_criteria": assessment_criteria_dict,
"course_dict": course_dict
}
# get chart data def get_column(assessment_criteria):
def get_chart():
grading_scale = frappe.db.get_value("Assessment Plan", list(assessment_plan_list)[0], "grading_scale")
grades = frappe.db.sql_list('''select grade_code from `tabGrading Scale Interval` where parent=%s''',
(grading_scale))
criteria_list = [d[0] for d in assessment_criteria_list] + ["Total"]
return get_chart_data(grades, criteria_list, kounter)
assessment_plan_list, assessment_criteria_list, total_maximum_score, grading_scale,\
student_group_list = get_assessment_details()
result_dict, kounter = get_result_map()
data = get_data()
columns = get_column(assessment_criteria_list, total_maximum_score)
chart = get_chart()
data_to_be_printed = [{
"assessment_plan": ", ".join(assessment_plan_list)
}]
return columns, data, None, chart, data_to_be_printed
def get_column(assessment_criteria, total_maximum_score):
columns = [{ columns = [{
"fieldname": "student", "fieldname": "student",
"label": _("Student ID"), "label": _("Student ID"),
@ -143,40 +203,28 @@ def get_column(assessment_criteria, total_maximum_score):
}] }]
for d in assessment_criteria: for d in assessment_criteria:
columns.append({ columns.append({
"fieldname": frappe.scrub(d[0]), "fieldname": frappe.scrub(d),
"label": d[0], "label": d,
"fieldtype": "Data", "fieldtype": "Data",
"width": 110 "width": 110
}) })
columns.append({ columns.append({
"fieldname": frappe.scrub(d[0]) +"_score", "fieldname": frappe.scrub(d) +"_score",
"label": "Score(" + str(int(d[1])) + ")", "label": "Score(" + str(int(assessment_criteria[d])) + ")",
"fieldtype": "Float", "fieldtype": "Float",
"width": 100 "width": 100
}) })
columns += [{
"fieldname": "total",
"label": "Total",
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "total_score",
"label": "Total Score("+ str(int(total_maximum_score)) + ")",
"fieldtype": "Float",
"width": 110
}]
return columns return columns
def get_chart_data(grades, assessment_criteria_list, kounter):
def get_chart_data(grades, criteria_list, kounter):
grades = sorted(grades) grades = sorted(grades)
datasets = [] datasets = []
for grade in grades: for grade in grades:
tmp = frappe._dict({"values":[], "title": grade}) tmp = frappe._dict({"values":[], "title": grade})
for criteria in assessment_criteria_list: for criteria in criteria_list:
if grade in kounter[criteria]: if grade in kounter[criteria]:
tmp["values"].append(kounter[criteria][grade]) tmp["values"].append(kounter[criteria][grade])
else: else:
@ -185,7 +233,7 @@ def get_chart_data(grades, assessment_criteria_list, kounter):
return { return {
"data": { "data": {
"labels": assessment_criteria_list, "labels": criteria_list,
"datasets": datasets "datasets": datasets
}, },
"type": 'bar', "type": 'bar',

View File

@ -0,0 +1,38 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Final Assessment Grades"] = {
"filters": [
{
"fieldname":"academic_year",
"label": __("Academic Year"),
"fieldtype": "Link",
"options": "Academic Year",
"reqd": 1
},
{
"fieldname":"student_group",
"label": __("Student Group"),
"fieldtype": "Link",
"options": "Student Group",
"reqd": 1,
"get_query": function() {
return{
filters: {
"group_based_on": "Batch",
"academic_year": frappe.query_report_filters_by_name.academic_year.value
}
};
}
},
{
"fieldname":"assessment_group",
"label": __("Assessment Group"),
"fieldtype": "Link",
"options": "Assessment Group",
"reqd": 1
}
]
}

View File

@ -0,0 +1,20 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2018-01-22 17:04:43.412054",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Shishuvan Secondary School",
"modified": "2018-01-22 17:04:43.412054",
"modified_by": "Administrator",
"module": "Education",
"name": "Final Assessment Grades",
"owner": "Administrator",
"ref_doctype": "Assessment Result",
"report_name": "Final Assessment Grades",
"report_type": "Script Report",
"roles": []
}

View File

@ -0,0 +1,85 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from collections import defaultdict
from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_formatted_result
from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_chart_data
def execute(filters=None):
columns, data, grades = [], [], []
args = frappe._dict()
course_wise_analysis = defaultdict(dict)
args["academic_year"] = filters.get("academic_year")
assessment_group = args["assessment_group"] = filters.get("assessment_group")
student_group = filters.get("student_group")
args.students = frappe.db.sql_list("select student from `tabStudent Group Student` where parent=%s", (student_group))
values = get_formatted_result(args, get_course=True)
student_details = values.get("student_details")
assessment_result = values.get("assessment_result")
course_dict = values.get("course_dict")
for student in args.students:
student_row = {}
student_row["student"] = student
student_row["student_name"] = student_details[student]
for course in course_dict:
scrub_course = frappe.scrub(course)
if assessment_group in assessment_result[student][course]:
student_row["grade_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["grade"]
student_row["score_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["score"]
# create the list of possible grades
if student_row["grade_" + scrub_course] not in grades:
grades.append(student_row["grade_" + scrub_course])
# create the dict of for gradewise analysis
if student_row["grade_" + scrub_course] not in course_wise_analysis[course]:
course_wise_analysis[course][student_row["grade_" + scrub_course]] = 1
else:
course_wise_analysis[course][student_row["grade_" + scrub_course]] += 1
data.append(student_row)
course_list = [d for d in course_dict]
columns = get_column(course_dict)
chart = get_chart_data(grades, course_list, course_wise_analysis)
return columns, data, None, chart
def get_column(course_dict):
columns = [{
"fieldname": "student",
"label": _("Student ID"),
"fieldtype": "Link",
"options": "Student",
"width": 90
},
{
"fieldname": "student_name",
"label": _("Student Name"),
"fieldtype": "Data",
"width": 160
}]
for course in course_dict:
columns.append({
"fieldname": "grade_" + frappe.scrub(course),
"label": course,
"fieldtype": "Data",
"width": 110
})
columns.append({
"fieldname": "score_" + frappe.scrub(course),
"label": "Score(" + str(course_dict[course]) + ")",
"fieldtype": "Float",
"width": 100
})
return columns

View File

@ -183,7 +183,6 @@ erpnext.patches.v5_0.index_on_account_and_gl_entry
execute:frappe.db.sql("""delete from `tabProject Task`""") execute:frappe.db.sql("""delete from `tabProject Task`""")
erpnext.patches.v5_0.update_item_desc_in_invoice erpnext.patches.v5_0.update_item_desc_in_invoice
erpnext.patches.v5_1.fix_against_account erpnext.patches.v5_1.fix_against_account
erpnext.patches.v5_1.fix_credit_days_based_on
execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True) execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True)
erpnext.patches.v5_1.rename_roles erpnext.patches.v5_1.rename_roles
erpnext.patches.v5_1.default_bom erpnext.patches.v5_1.default_bom
@ -489,4 +488,6 @@ erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
erpnext.patches.v10_0.fichier_des_ecritures_comptables_for_france erpnext.patches.v10_0.fichier_des_ecritures_comptables_for_france
erpnext.patches.v10_0.update_assessment_plan erpnext.patches.v10_0.update_assessment_plan
erpnext.patches.v10_0.update_assessment_result erpnext.patches.v10_0.update_assessment_result
erpnext.patches.v10_0.added_extra_gst_custom_field
erpnext.patches.v10_0.workflow_leave_application #2018-01-24 erpnext.patches.v10_0.workflow_leave_application #2018-01-24
erpnext.patches.v10_0.set_default_payment_terms_based_on_company

View File

@ -0,0 +1,9 @@
import frappe
from erpnext.regional.india.setup import make_custom_fields
def execute():
company = frappe.get_all('Company', filters = {'country': 'India'})
if not company:
return
make_custom_fields()

View File

@ -0,0 +1,37 @@
from __future__ import unicode_literals
import frappe
from erpnext.patches.v8_10.change_default_customer_credit_days import make_payment_term, make_template
def execute():
for dt in ("Company", "Customer Group"):
frappe.reload_doc("setup", "doctype", frappe.scrub(dt))
credit_records = frappe.db.sql("""
SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
from `tab{0}`
where
((credit_days_based_on='Fixed Days' or credit_days_based_on is null) and credit_days is not null)
or credit_days_based_on='Last Day of the Next Month'
""".format(dt), as_dict=1)
for d in credit_records:
template = create_payment_terms_template(d)
frappe.db.sql("""
update `tab{0}`
set `payment_terms` = %s
where name = %s
""".format(dt), (template.name, d.name))
def create_payment_terms_template(data):
if data.credit_days_based_on == "Fixed Days":
pyt_template_name = 'Default Payment Term - N{0}'.format(data.credit_days)
else:
pyt_template_name = 'Default Payment Term - EO2M'
if not frappe.db.exists("Payment Terms Template", pyt_template_name):
payment_term = make_payment_term(data.credit_days, data.credit_days_based_on)
template = make_template(payment_term)
else:
template = frappe.get_doc("Payment Terms Template", pyt_template_name)
return template

View File

@ -9,4 +9,5 @@ def execute():
frappe.reload_doc("hr", "doctype", "leave_application") frappe.reload_doc("hr", "doctype", "leave_application")
frappe.reload_doc("workflow", "doctype", "workflow") frappe.reload_doc("workflow", "doctype", "workflow")
leave_application_workflow() leave_application_workflow()
if frappe.db.has_column("Leave Application", "status"):
frappe.db.sql("""update `tabLeave Application` set workflow_state = status""") frappe.db.sql("""update `tabLeave Application` set workflow_state = status""")

View File

@ -1,9 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
for dt in ("Customer", "Customer Group", "Company"):
frappe.reload_doctype(dt, force=True)
frappe.db.sql("""update `tab{0}` set credit_days_based_on='Fixed Days'
where ifnull(credit_days, 0) > 0""".format(dt))

View File

@ -17,7 +17,8 @@ def execute():
SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name` SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
from `tab{0}` from `tab{0}`
where where
(credit_days_based_on='Fixed Days' and credit_days is not null) ((credit_days_based_on='Fixed Days' or credit_days_based_on is null)
and credit_days is not null)
or credit_days_based_on='Last Day of the Next Month' or credit_days_based_on='Last Day of the Next Month'
""".format(doctype)) """.format(doctype))

View File

@ -523,7 +523,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}, },
callback: function(r, rt) { callback: function(r, rt) {
if(r.message) { if(r.message) {
me.frm.set_value("due_date", r.message); me.frm.doc.due_date = r.message;
refresh_field("due_date");
frappe.ui.form.trigger(me.frm.doc.doctype, "currency"); frappe.ui.form.trigger(me.frm.doc.doctype, "currency");
me.recalculate_terms(); me.recalculate_terms();
} }
@ -538,7 +539,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
due_date: function() { due_date: function() {
// due_date is to be changed, payment terms template and/or payment schedule must // due_date is to be changed, payment terms template and/or payment schedule must
// be removed as due_date is automatically changed based on payment terms // be removed as due_date is automatically changed based on payment terms
if (this.frm.doc.due_date) { if (this.frm.doc.due_date && !this.frm.updating_party_details && !this.frm.doc.is_pos) {
if (this.frm.doc.payment_terms_template || if (this.frm.doc.payment_terms_template ||
(this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length)) { (this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length)) {
var message1 = ""; var message1 = "";
@ -555,10 +556,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if (message1.length !== 0) message2 = " and " + message2; if (message1.length !== 0) message2 = " and " + message2;
final_message = final_message + message2; final_message = final_message + message2;
} }
frappe.msgprint(final_message); frappe.msgprint(final_message);
} }
} }
}, },

View File

@ -103,7 +103,20 @@ def make_custom_fields():
depends_on='eval:in_list(["SEZ", "Export", "Deemed Export"], doc.invoice_type)', depends_on='eval:in_list(["SEZ", "Export", "Deemed Export"], doc.invoice_type)',
options='\nWith Payment of Tax\nWithout Payment of Tax'), options='\nWith Payment of Tax\nWithout Payment of Tax'),
dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN', dict(fieldname='ecommerce_gstin', label='E-commerce GSTIN',
fieldtype='Data', insert_after='export_type', print_hide=1) fieldtype='Data', insert_after='export_type', print_hide=1),
dict(fieldname='reason_for_issuing_document', label='Reason For Issuing document',
fieldtype='Select', insert_after='ecommerce_gstin', print_hide=1,
depends_on='eval:doc.is_return==1',
options='\n01-Sales Return\n02-Post Sale Discount\n03-Deficiency in services\n04-Correction in Invoice\n05-Change in POS\n06-Finalization of Provisional assessment\n07-Others'),
dict(fieldname='port_code', label='Port Code',
fieldtype='Data', insert_after='reason_for_issuing_document', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
dict(fieldname='shipping_bill_number', label=' Shipping Bill Number',
fieldtype='Data', insert_after='port_code', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
dict(fieldname='shipping_bill_date', label='Shipping Bill Date',
fieldtype='Date', insert_after='shipping_bill_number', print_hide=1,
depends_on="eval:doc.invoice_type=='Export' "),
] ]
purchase_invoice_gst_fields = [ purchase_invoice_gst_fields = [

View File

@ -31,7 +31,7 @@ frappe.query_reports["GSTR-1"] = {
"label": __("Type of Business"), "label": __("Type of Business"),
"fieldtype": "Select", "fieldtype": "Select",
"reqd": 1, "reqd": 1,
"options": ["B2B", "B2C Large", "B2C Small"], "options": ["B2B", "B2C Large", "B2C Small","CDNR", "EXPORT"],
"default": "B2B" "default": "B2B"
} }
] ]

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe, json import frappe, json
from frappe import _ from frappe import _
from datetime import date
def execute(filters=None): def execute(filters=None):
return Gstr1Report(filters).run() return Gstr1Report(filters).run()
@ -35,13 +36,15 @@ class Gstr1Report(object):
for rate, items in items_based_on_rate.items(): for rate, items in items_based_on_rate.items():
row = [] row = []
for fieldname in invoice_fields: for fieldname in invoice_fields:
if fieldname == "invoice_value": if self.filters.get("type_of_business") == "CDNR" and fieldname == "invoice_value":
row.append(abs(invoice_details.base_rounded_total) or abs(invoice_details.base_grand_total))
elif fieldname == "invoice_value":
row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total) row.append(invoice_details.base_rounded_total or invoice_details.base_grand_total)
else: else:
row.append(invoice_details.get(fieldname)) row.append(invoice_details.get(fieldname))
row += [rate, row += [rate,
sum([net_amount for item_code, net_amount in self.invoice_items.get(inv).items() sum([abs(net_amount) for item_code, net_amount in self.invoice_items.get(inv).items()
if item_code in items]), if item_code in items]),
self.invoice_cess.get(inv) self.invoice_cess.get(inv)
] ]
@ -49,6 +52,10 @@ class Gstr1Report(object):
if self.filters.get("type_of_business") == "B2C Small": if self.filters.get("type_of_business") == "B2C Small":
row.append("E" if invoice_details.ecommerce_gstin else "OE") row.append("E" if invoice_details.ecommerce_gstin else "OE")
if self.filters.get("type_of_business") == "CDNR":
row.append("Y" if invoice_details.posting_date <= date(2017, 7, 1) else "N")
row.append("C" if invoice_details.return_against else "R")
self.data.append(row) self.data.append(row)
def get_invoice_data(self): def get_invoice_data(self):
@ -66,7 +73,15 @@ class Gstr1Report(object):
place_of_supply, place_of_supply,
ecommerce_gstin, ecommerce_gstin,
reverse_charge, reverse_charge,
invoice_type invoice_type,
return_against,
is_return,
invoice_type,
export_type,
port_code,
shipping_bill_number,
shipping_bill_date,
reason_for_issuing_document
from `tabSales Invoice` from `tabSales Invoice`
where docstatus = 1 %s where docstatus = 1 %s
order by posting_date desc order by posting_date desc
@ -85,18 +100,27 @@ class Gstr1Report(object):
conditions += opts[1] conditions += opts[1]
customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type}) customers = frappe.get_all("Customer", filters={"customer_type": self.customer_type})
conditions += " and customer in ('{0}')".format("', '".join([frappe.db.escape(c.name)
for c in customers])) if self.filters.get("type_of_business") == "B2B":
conditions += " and invoice_type != 'Export' and is_return != 1 and customer in ('{0}')".\
format("', '".join([frappe.db.escape(c.name) for c in customers]))
if self.filters.get("type_of_business") == "B2C Large": if self.filters.get("type_of_business") == "B2C Large":
conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2)
and grand_total > 250000""" and grand_total > 250000 and is_return != 1 and customer in ('{0}')""".\
format("', '".join([frappe.db.escape(c.name) for c in customers]))
elif self.filters.get("type_of_business") == "B2C Small": elif self.filters.get("type_of_business") == "B2C Small":
conditions += """ and ( conditions += """ and (
SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2) SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2)
or grand_total <= 250000 or grand_total <= 250000 ) and is_return != 1 and customer in ('{0}')""".\
)""" format("', '".join([frappe.db.escape(c.name) for c in customers]))
elif self.filters.get("type_of_business") == "CDNR":
conditions += """ and is_return = 1 """
elif self.filters.get("type_of_business") == "EXPORT":
conditions += """ and is_return !=1 and invoice_type = 'Export' """
return conditions return conditions
def get_invoice_items(self): def get_invoice_items(self):
@ -118,7 +142,7 @@ class Gstr1Report(object):
where where
parenttype = 'Sales Invoice' and docstatus = 1 parenttype = 'Sales Invoice' and docstatus = 1
and parent in (%s) and parent in (%s)
and tax_amount_after_discount_amount > 0
order by account_head order by account_head
""" % (', '.join(['%s']*len(self.invoices.keys()))), tuple(self.invoices.keys())) """ % (', '.join(['%s']*len(self.invoices.keys()))), tuple(self.invoices.keys()))
@ -152,7 +176,6 @@ class Gstr1Report(object):
.setdefault(tax_rate, []) .setdefault(tax_rate, [])
if item_code not in rate_based_dict: if item_code not in rate_based_dict:
rate_based_dict.append(item_code) rate_based_dict.append(item_code)
except ValueError: except ValueError:
continue continue
if unidentified_gst_accounts: if unidentified_gst_accounts:
@ -185,12 +208,6 @@ class Gstr1Report(object):
"label": "Taxable Value", "label": "Taxable Value",
"fieldtype": "Currency", "fieldtype": "Currency",
"width": 100 "width": 100
},
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
} }
] ]
self.other_columns = [] self.other_columns = []
@ -200,33 +217,39 @@ class Gstr1Report(object):
{ {
"fieldname": "customer_gstin", "fieldname": "customer_gstin",
"label": "GSTIN/UIN of Recipient", "label": "GSTIN/UIN of Recipient",
"fieldtype": "Data" "fieldtype": "Data",
"width": 150
}, },
{ {
"fieldname": "customer_name", "fieldname": "customer_name",
"label": "Receiver Name", "label": "Receiver Name",
"fieldtype": "Data" "fieldtype": "Data",
"width":100
}, },
{ {
"fieldname": "invoice_number", "fieldname": "invoice_number",
"label": "Invoice Number", "label": "Invoice Number",
"fieldtype": "Link", "fieldtype": "Link",
"options": "Sales Invoice" "options": "Sales Invoice",
"width":100
}, },
{ {
"fieldname": "posting_date", "fieldname": "posting_date",
"label": "Invoice date", "label": "Invoice date",
"fieldtype": "Date" "fieldtype": "Date",
"width":80
}, },
{ {
"fieldname": "invoice_value", "fieldname": "invoice_value",
"label": "Invoice Value", "label": "Invoice Value",
"fieldtype": "Currency" "fieldtype": "Currency",
"width":100
}, },
{ {
"fieldname": "place_of_supply", "fieldname": "place_of_supply",
"label": "Place of Supply", "label": "Place of Supply",
"fieldtype": "Data" "fieldtype": "Data",
"width":100
}, },
{ {
"fieldname": "reverse_charge", "fieldname": "reverse_charge",
@ -241,9 +264,19 @@ class Gstr1Report(object):
{ {
"fieldname": "ecommerce_gstin", "fieldname": "ecommerce_gstin",
"label": "E-Commerce GSTIN", "label": "E-Commerce GSTIN",
"fieldtype": "Data" "fieldtype": "Data",
"width":120
} }
] ]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
}
]
elif self.filters.get("type_of_business") == "B2C Large": elif self.filters.get("type_of_business") == "B2C Large":
self.invoice_columns = [ self.invoice_columns = [
{ {
@ -278,6 +311,93 @@ class Gstr1Report(object):
"width": 130 "width": 130
} }
] ]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
}
]
elif self.filters.get("type_of_business") == "CDNR":
self.invoice_columns = [
{
"fieldname": "customer_gstin",
"label": "GSTIN/UIN of Recipient",
"fieldtype": "Data",
"width": 150
},
{
"fieldname": "customer_name",
"label": "Receiver Name",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "return_against",
"label": "Invoice/Advance Receipt Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width": 120
},
{
"fieldname": "posting_date",
"label": "Invoice/Advance Receipt date",
"fieldtype": "Date",
"width": 120
},
{
"fieldname": "invoice_number",
"label": "Invoice/Advance Receipt Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width":120
},
{
"fieldname": "posting_date",
"label": "Invoice/Advance Receipt date",
"fieldtype": "Date",
"width": 120
},
{
"fieldname": "reason_for_issuing_document",
"label": "Reason For Issuing document",
"fieldtype": "Data",
"width": 140
},
{
"fieldname": "place_of_supply",
"label": "Place of Supply",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width": 120
}
]
self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
},
{
"fieldname": "pre_gst",
"label": "PRE GST",
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "document_type",
"label": "Document Type",
"fieldtype": "Data",
"width": 80
}
]
elif self.filters.get("type_of_business") == "B2C Small": elif self.filters.get("type_of_business") == "B2C Small":
self.invoice_columns = [ self.invoice_columns = [
{ {
@ -294,6 +414,12 @@ class Gstr1Report(object):
} }
] ]
self.other_columns = [ self.other_columns = [
{
"fieldname": "cess_amount",
"label": "Cess Amount",
"fieldtype": "Currency",
"width": 100
},
{ {
"fieldname": "type", "fieldname": "type",
"label": "Type", "label": "Type",
@ -301,4 +427,50 @@ class Gstr1Report(object):
"width": 50 "width": 50
} }
] ]
elif self.filters.get("type_of_business") == "EXPORT":
self.invoice_columns = [
{
"fieldname": "export_type",
"label": "Export Type",
"fieldtype": "Data",
"width":120
},
{
"fieldname": "invoice_number",
"label": "Invoice Number",
"fieldtype": "Link",
"options": "Sales Invoice",
"width":120
},
{
"fieldname": "posting_date",
"label": "Invoice date",
"fieldtype": "Date",
"width": 120
},
{
"fieldname": "invoice_value",
"label": "Invoice Value",
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "port_code",
"label": "Port Code",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "shipping_bill_number",
"label": "Shipping Bill Number",
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "shipping_bill_date",
"label": "Shipping Bill Date",
"fieldtype": "Date",
"width": 120
}
]
self.columns = self.invoice_columns + self.tax_columns + self.other_columns self.columns = self.invoice_columns + self.tax_columns + self.other_columns

View File

@ -567,9 +567,11 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
target.base_amount = target.amount * flt(source_parent.conversion_rate) target.base_amount = target.amount * flt(source_parent.conversion_rate)
target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty target.qty = target.amount / flt(source.rate) if (source.rate and source.billed_amt) else source.qty
if source_parent.project:
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center")
if not target.cost_center and target.item_code:
item = frappe.db.get_value("Item", target.item_code, ["item_group", "selling_cost_center"], as_dict=1) item = frappe.db.get_value("Item", target.item_code, ["item_group", "selling_cost_center"], as_dict=1)
target.cost_center = frappe.db.get_value("Project", source_parent.project, "cost_center") \ target.cost_center = item.selling_cost_center \
or item.selling_cost_center \
or frappe.db.get_value("Item Group", item.item_group, "default_cost_center") or frappe.db.get_value("Item Group", item.item_group, "default_cost_center")
doclist = get_mapped_doc("Sales Order", source_name, { doclist = get_mapped_doc("Sales Order", source_name, {

View File

@ -340,7 +340,8 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) { conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
this._super(doc, cdt, cdn, dont_fetch_price_list_rate); this._super(doc, cdt, cdn, dont_fetch_price_list_rate);
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) { if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) &&
in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
this.set_batch_number(cdt, cdn); this.set_batch_number(cdt, cdn);
} }
}, },

View File

@ -1180,6 +1180,35 @@
"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": "column_break_26",
"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_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -1219,8 +1248,9 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "column_break_26", "depends_on": "",
"fieldtype": "Column Break", "fieldname": "payment_terms",
"fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@ -1228,8 +1258,10 @@
"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": "Default Payment Terms Template",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "Payment Terms Template",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -1242,69 +1274,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": "credit_days_based_on",
"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": "Credit Days Based On",
"length": 0,
"no_copy": 0,
"options": "\nFixed Days\nLast Day of the Next Month",
"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": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')",
"fieldname": "credit_days",
"fieldtype": "Int",
"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": "Credit Days",
"length": 0,
"no_copy": 0,
"oldfieldname": "credit_days",
"oldfieldtype": "Int",
"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": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_on_submit": 0, "allow_on_submit": 0,
@ -2052,7 +2021,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2017-12-07 18:40:24.646920", "modified": "2018-01-29 12:40:24.646920",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@ -162,12 +162,14 @@
"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, "columns": 0,
"fieldname": "credit_days_based_on", "depends_on": "",
"fieldtype": "Select", "fieldname": "payment_terms",
"fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
@ -175,10 +177,10 @@
"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": "Credit Days Based On", "label": "Default Payment Terms Template",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "\nFixed Days\nLast Day of the Next Month", "options": "Payment Terms Template",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -191,35 +193,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
"fieldname": "credit_days",
"fieldtype": "Int",
"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": "Credit Days",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"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_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -411,7 +384,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2017-02-20 13:25:31.549874", "modified": "2018-01-29 13:25:31.549874",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Customer Group", "name": "Customer Group",

View File

@ -128,7 +128,7 @@ def insert_record(records):
doc.update(r) doc.update(r)
try: try:
doc.insert(ignore_permissions=True) doc.insert(ignore_permissions=True)
except frappe.DuplicateEntryError, e: except frappe.DuplicateEntryError as e:
# pass DuplicateEntryError and continue # pass DuplicateEntryError and continue
if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name: if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
# make sure DuplicateEntryError is for the exact same doc and not a related doc # make sure DuplicateEntryError is for the exact same doc and not a related doc

View File

@ -86,6 +86,7 @@ erpnext.stock.ItemDashboard = Class.extend({
get_item_dashboard_data: function(data, max_count, show_item) { get_item_dashboard_data: function(data, max_count, show_item) {
if(!max_count) max_count = 0; if(!max_count) max_count = 0;
if(!data) data = []; if(!data) data = [];
data.forEach(function(d) { data.forEach(function(d) {
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production; d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production;
d.pending_qty = 0; d.pending_qty = 0;
@ -97,9 +98,16 @@ erpnext.stock.ItemDashboard = Class.extend({
max_count = Math.max(d.actual_or_pending, d.actual_qty, max_count = Math.max(d.actual_or_pending, d.actual_qty,
d.total_reserved, max_count); d.total_reserved, max_count);
}); });
var can_write = 0;
if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
can_write = 1;
}
return { return {
data: data, data: data,
max_count: max_count, max_count: max_count,
can_write:can_write,
show_item: show_item || false show_item: show_item || false
} }
} }

View File

@ -39,6 +39,7 @@
</span> </span>
</span> </span>
</div> </div>
{% if can_write %}
<div class="col-sm-2 text-right" style="margin-top: 8px;"> <div class="col-sm-2 text-right" style="margin-top: 8px;">
{% if d.actual_qty %} {% if d.actual_qty %}
<button class="btn btn-default btn-xs btn-move" <button class="btn btn-default btn-xs btn-move"
@ -52,6 +53,7 @@
data-item="{{ d.item_code }}" data-item="{{ d.item_code }}"
data-rate="{{ d.valuation_rate }}">{{ __("Add") }}</a> data-rate="{{ d.valuation_rate }}">{{ __("Add") }}</a>
</div> </div>
{% endif %}
</div> </div>
</div> </div>
{% endfor %} {% endfor %}

View File

@ -5,24 +5,31 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname, revert_series_if_last
from frappe.utils import flt, cint from frappe.utils import flt, cint
class UnableToSelectBatchError(frappe.ValidationError): pass class UnableToSelectBatchError(frappe.ValidationError):
pass
def get_name_from_naming_series(): def get_name_from_naming_series():
naming_series_prefix = frappe.db.get_single_value('Stock Settings', 'naming_series_prefix') """
if not naming_series_prefix: Get a name generated for a Batch from the Batch's naming series.
naming_series_prefix = 'BATCH-' :return: The string that was generated.
"""
name = make_autoname(naming_series_prefix + '.#####') naming_series_prefix = _get_batch_prefix()
key = _make_naming_series_key(naming_series_prefix)
name = make_autoname(key)
return name return name
def get_name_from_hash(): def get_name_from_hash():
"""
Get a name for a Batch by generating a unique hash.
:return: The hash that was generated.
"""
temp = None temp = None
while not temp: while not temp:
temp = frappe.generate_hash()[:7].upper() temp = frappe.generate_hash()[:7].upper()
@ -32,13 +39,66 @@ def get_name_from_hash():
return temp return temp
def batch_uses_naming_series():
"""
Verify if the Batch is to be named using a naming series
:return: bool
"""
use_naming_series = cint(frappe.db.get_single_value('Stock Settings', 'use_naming_series'))
return bool(use_naming_series)
def _get_batch_prefix():
"""
Get the naming series prefix set in Stock Settings.
It does not do any sanity checks so make sure to use it after checking if the Batch
is set to use naming series.
:return: The naming series.
"""
naming_series_prefix = frappe.db.get_single_value('Stock Settings', 'naming_series_prefix')
if not naming_series_prefix:
naming_series_prefix = 'BATCH-'
return naming_series_prefix
def _make_naming_series_key(prefix):
"""
Make naming series key for a Batch.
Naming series key is in the format [prefix].[#####]
:param prefix: Naming series prefix gotten from Stock Settings
:return: The derived key. If no prefix is given, an empty string is returned
"""
if not unicode(prefix):
return ''
else:
return prefix.upper() + '.#####'
def get_batch_naming_series():
"""
Get naming series key for a Batch.
Naming series key is in the format [prefix].[#####]
:return: The naming series or empty string if not available
"""
series = ''
if batch_uses_naming_series():
prefix = _get_batch_prefix()
key = _make_naming_series_key(prefix)
series = key
return series
class Batch(Document): class Batch(Document):
def autoname(self): def autoname(self):
"""Generate random ID for batch if not specified""" """Generate random ID for batch if not specified"""
if not self.batch_id: if not self.batch_id:
if frappe.db.get_value('Item', self.item, 'create_new_batch'): if frappe.db.get_value('Item', self.item, 'create_new_batch'):
use_naming_series = frappe.db.get_single_value('Stock Settings', 'use_naming_series') if batch_uses_naming_series():
if use_naming_series:
self.batch_id = get_name_from_naming_series() self.batch_id = get_name_from_naming_series()
else: else:
self.batch_id = get_name_from_hash() self.batch_id = get_name_from_hash()
@ -50,13 +110,17 @@ class Batch(Document):
def onload(self): def onload(self):
self.image = frappe.db.get_value('Item', self.item, 'image') self.image = frappe.db.get_value('Item', self.item, 'image')
def after_delete(self):
revert_series_if_last(get_batch_naming_series(), self.name)
def validate(self): def validate(self):
self.item_has_batch_enabled() self.item_has_batch_enabled()
def item_has_batch_enabled(self): def item_has_batch_enabled(self):
if frappe.db.get_value("Item",self.item,"has_batch_no") == 0: if frappe.db.get_value("Item", self.item, "has_batch_no") == 0:
frappe.throw(_("The selected item cannot have Batch")) frappe.throw(_("The selected item cannot have Batch"))
@frappe.whitelist() @frappe.whitelist()
def get_batch_qty(batch_no=None, warehouse=None, item_code=None): def get_batch_qty(batch_no=None, warehouse=None, item_code=None):
"""Returns batch actual qty if warehouse is passed, """Returns batch actual qty if warehouse is passed,
@ -89,16 +153,18 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None):
return out return out
@frappe.whitelist() @frappe.whitelist()
def get_batches_by_oldest(item_code, warehouse): def get_batches_by_oldest(item_code, warehouse):
"""Returns the oldest batch and qty for the given item_code and warehouse""" """Returns the oldest batch and qty for the given item_code and warehouse"""
batches = get_batch_qty(item_code = item_code, warehouse = warehouse) batches = get_batch_qty(item_code=item_code, warehouse=warehouse)
batches_dates = [[batch, frappe.get_value('Batch', batch.batch_no, 'expiry_date')] for batch in batches] batches_dates = [[batch, frappe.get_value('Batch', batch.batch_no, 'expiry_date')] for batch in batches]
batches_dates.sort(key=lambda tup: tup[1]) batches_dates.sort(key=lambda tup: tup[1])
return batches_dates return batches_dates
@frappe.whitelist() @frappe.whitelist()
def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None): def split_batch(batch_no, item_code, warehouse, qty, new_batch_id=None):
"""Split the batch into a new batch""" """Split the batch into a new batch"""
batch = frappe.get_doc(dict(doctype='Batch', item=item_code, batch_id=new_batch_id)).insert() batch = frappe.get_doc(dict(doctype='Batch', item=item_code, batch_id=new_batch_id)).insert()
stock_entry = frappe.get_doc(dict( stock_entry = frappe.get_doc(dict(
@ -106,16 +172,16 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
purpose='Repack', purpose='Repack',
items=[ items=[
dict( dict(
item_code = item_code, item_code=item_code,
qty = float(qty or 0), qty=float(qty or 0),
s_warehouse = warehouse, s_warehouse=warehouse,
batch_no = batch_no batch_no=batch_no
), ),
dict( dict(
item_code = item_code, item_code=item_code,
qty = float(qty or 0), qty=float(qty or 0),
t_warehouse = warehouse, t_warehouse=warehouse,
batch_no = batch.name batch_no=batch.name
), ),
] ]
)) ))
@ -124,7 +190,8 @@ def split_batch(batch_no, item_code, warehouse, qty, new_batch_id = None):
return batch.name return batch.name
def set_batch_nos(doc, warehouse_field, throw = False):
def set_batch_nos(doc, warehouse_field, throw=False):
"""Automatically select `batch_no` for outgoing items in item table""" """Automatically select `batch_no` for outgoing items in item table"""
for d in doc.items: for d in doc.items:
qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0 qty = d.get('stock_qty') or d.get('transfer_qty') or d.get('qty') or 0
@ -135,8 +202,9 @@ def set_batch_nos(doc, warehouse_field, throw = False):
d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw) d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw)
else: else:
batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse) batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
if flt(batch_qty) < flt(qty): if flt(batch_qty, d.precision("qty")) < flt(qty, d.precision("qty")):
frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, d.qty)) frappe.throw(_("Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches").format(d.idx, d.batch_no, batch_qty, d.stock_qty))
@frappe.whitelist() @frappe.whitelist()
def get_batch_no(item_code, warehouse, qty=1, throw=False): def get_batch_no(item_code, warehouse, qty=1, throw=False):

View File

@ -1,7 +1,7 @@
frappe.listview_settings['Batch'] = { frappe.listview_settings['Batch'] = {
add_fields: ["item", "expiry_date"], add_fields: ["item", "expiry_date"],
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date) <= 0) { if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) {
return [__("Expired"), "red", "expiry_date,>=,Today"] return [__("Expired"), "red", "expiry_date,>=,Today"]
} else if(doc.expiry_date) { } else if(doc.expiry_date) {
return [__("Not Expired"), "green", "expiry_date,<,Today"] return [__("Not Expired"), "green", "expiry_date,<,Today"]

View File

@ -7,6 +7,8 @@ from frappe.exceptions import ValidationError
import unittest import unittest
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
from frappe.utils import cint
class TestBatch(unittest.TestCase): class TestBatch(unittest.TestCase):
@ -21,7 +23,7 @@ class TestBatch(unittest.TestCase):
def make_batch_item(cls, item_name): def make_batch_item(cls, item_name):
from erpnext.stock.doctype.item.test_item import make_item from erpnext.stock.doctype.item.test_item import make_item
if not frappe.db.exists(item_name): if not frappe.db.exists(item_name):
make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1)) return make_item(item_name, dict(has_batch_no = 1, create_new_batch = 1))
def test_purchase_receipt(self, batch_qty = 100): def test_purchase_receipt(self, batch_qty = 100):
'''Test automated batch creation from Purchase Receipt''' '''Test automated batch creation from Purchase Receipt'''
@ -192,3 +194,37 @@ class TestBatch(unittest.TestCase):
] ]
)).insert() )).insert()
stock_entry.submit() stock_entry.submit()
def test_batch_name_with_naming_series(self):
stock_settings = frappe.get_single('Stock Settings')
use_naming_series = cint(stock_settings.use_naming_series)
if not use_naming_series:
frappe.set_value('Stock Settings', 'Stock Settings', 'use_naming_series', 1)
batch = self.make_new_batch('_Test Stock Item For Batch Test1')
batch_name = batch.name
self.assertTrue(batch_name.startswith('BATCH-'))
batch.delete()
batch = self.make_new_batch('_Test Stock Item For Batch Test2')
self.assertEqual(batch_name, batch.name)
# reset Stock Settings
if not use_naming_series:
frappe.set_value('Stock Settings', 'Stock Settings', 'use_naming_series', 0)
def make_new_batch(self, item_name, batch_id=None, do_not_insert=0):
batch = frappe.new_doc('Batch')
item = self.make_batch_item(item_name)
batch.item = item.name
if batch_id:
batch.batch_id = batch_id
if not do_not_insert:
batch.insert()
return batch

View File

@ -293,7 +293,7 @@ class StockEntry(StockController):
# get basic rate # get basic rate
if not d.bom_no: if not d.bom_no:
if not flt(d.basic_rate) or d.s_warehouse or force: if (not flt(d.basic_rate) and not d.allow_zero_valuation_rate) or d.s_warehouse or force:
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
if basic_rate > 0: if basic_rate > 0:
d.basic_rate = basic_rate d.basic_rate = basic_rate
@ -304,7 +304,8 @@ class StockEntry(StockController):
# get scrap items basic rate # get scrap items basic rate
if d.bom_no: if d.bom_no:
if not flt(d.basic_rate) and getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse: if not flt(d.basic_rate) and not d.allow_zero_valuation_rate and \
getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d)) basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
if basic_rate > 0: if basic_rate > 0:
d.basic_rate = basic_rate d.basic_rate = basic_rate

View File

@ -40,12 +40,12 @@ def create_customers(args_data):
@frappe.whitelist() @frappe.whitelist()
def create_letterhead(args_data): def create_letterhead(args_data):
args = json.loads(args_data) args = json.loads(args_data)
letterhead = args.get("letterhead").encode('utf-8') letterhead = args.get("letterhead")
if letterhead: if letterhead:
try: try:
frappe.get_doc({ frappe.get_doc({
"doctype":"Letter Head", "doctype":"Letter Head",
"content":"""<div><img src="{0}" style='max-width: 100%%;'><br></div>""".format(letterhead), "content":"""<div><img src="{0}" style='max-width: 100%%;'><br></div>""".format(letterhead.encode('utf-8')),
"letter_head_name": _("Standard"), "letter_head_name": _("Standard"),
"is_default": 1 "is_default": 1
}).insert() }).insert()