Merge branch 'develop' into barredterra-patch-1
This commit is contained in:
commit
94308fc107
@ -31,8 +31,7 @@ frappe.ui.form.on('POS Profile', {
|
||||
frm.set_query("print_format", function() {
|
||||
return {
|
||||
filters: [
|
||||
['Print Format', 'doc_type', '=', 'Sales Invoice'],
|
||||
['Print Format', 'print_format_type', '=', 'Jinja'],
|
||||
['Print Format', 'doc_type', '=', 'POS Invoice']
|
||||
]
|
||||
};
|
||||
});
|
||||
@ -45,10 +44,6 @@ frappe.ui.form.on('POS Profile', {
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("print_format", function() {
|
||||
return { filters: { doc_type: "Sales Invoice", print_format_type: "JS"} };
|
||||
});
|
||||
|
||||
frm.set_query('company_address', function(doc) {
|
||||
if(!doc.company) {
|
||||
frappe.throw(__('Please set Company'));
|
||||
|
@ -0,0 +1,89 @@
|
||||
<h1 class="text-center" style="page-break-before:always">{{ filters.party[0] }}</h1>
|
||||
<h3 class="text-center">{{ _("Statement of Accounts") }}</h3>
|
||||
|
||||
<h5 class="text-center">
|
||||
{{ frappe.format(filters.from_date, 'Date')}}
|
||||
{{ _("to") }}
|
||||
{{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">{{ _("Date") }}</th>
|
||||
<th style="width: 15%">{{ _("Ref") }}</th>
|
||||
<th style="width: 25%">{{ _("Party") }}</th>
|
||||
<th style="width: 15%">{{ _("Debit") }}</th>
|
||||
<th style="width: 15%">{{ _("Credit") }}</th>
|
||||
<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% if(row.posting_date) %}
|
||||
<td>{{ frappe.format(row.posting_date, 'Date') }}</td>
|
||||
<td>{{ row.voucher_type }}
|
||||
<br>{{ row.voucher_no }}</td>
|
||||
<td>
|
||||
{% if not (filters.party or filters.account) %}
|
||||
{{ row.party or row.account }}
|
||||
<br>
|
||||
{% endif %}
|
||||
|
||||
{{ _("Against") }}: {{ row.against }}
|
||||
<br>{{ _("Remarks") }}: {{ row.remarks }}
|
||||
{% if row.bill_no %}
|
||||
<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}</td>
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}</td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td><b>{{ frappe.format(row.account, {fieldtype: "Link"}) or " " }}</b></td>
|
||||
<td style="text-align: right">
|
||||
{{ row.account and frappe.utils.fmt_money(row.debit, filters.presentation_currency) }}
|
||||
</td>
|
||||
<td style="text-align: right">
|
||||
{{ row.account and frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}
|
||||
</td>
|
||||
{% endif %}
|
||||
<td style="text-align: right">
|
||||
{{ frappe.utils.fmt_money(row.balance, filters.presentation_currency) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br><br>
|
||||
{% if aging %}
|
||||
<h3 class="text-center">{{ _("Ageing Report Based On ") }} {{ aging.ageing_based_on }}</h3>
|
||||
<h5 class="text-center">
|
||||
{{ _("Up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
<br>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">30 Days</th>
|
||||
<th style="width: 15%">60 Days</th>
|
||||
<th style="width: 25%">90 Days</th>
|
||||
<th style="width: 15%">120 Days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ aging.range1 }}</td>
|
||||
<td>{{ aging.range2 }}</td>
|
||||
<td>{{ aging.range3 }}</td>
|
||||
<td>{{ aging.range4 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<p class="text-right text-muted">Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}</p>
|
@ -0,0 +1,132 @@
|
||||
// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Process Statement Of Accounts', {
|
||||
view_properties: function(frm) {
|
||||
frappe.route_options = {doc_type: 'Customer'};
|
||||
frappe.set_route("Form", "Customize Form");
|
||||
},
|
||||
refresh: function(frm){
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button('Send Emails',function(){
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
|
||||
args: {
|
||||
"document_name": frm.doc.name,
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r && r.message) {
|
||||
frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'});
|
||||
}
|
||||
else{
|
||||
frappe.msgprint('No Records for these settings.')
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
frm.add_custom_button('Download',function(){
|
||||
var url = frappe.urllib.get_full_url(
|
||||
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
|
||||
+ 'document_name='+encodeURIComponent(frm.doc.name))
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'GET',
|
||||
success: function(result) {
|
||||
if(jQuery.isEmptyObject(result)){
|
||||
frappe.msgprint('No Records for these settings.');
|
||||
}
|
||||
else{
|
||||
window.location = url;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
onload: function(frm) {
|
||||
frm.set_query('currency', function(){
|
||||
return {
|
||||
filters: {
|
||||
'enabled': 1
|
||||
}
|
||||
}
|
||||
});
|
||||
if(frm.doc.__islocal){
|
||||
frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1));
|
||||
frm.set_value('to_date', frappe.datetime.get_today());
|
||||
}
|
||||
},
|
||||
customer_collection: function(frm){
|
||||
frm.set_value('collection_name', '');
|
||||
if(frm.doc.customer_collection){
|
||||
frm.get_field('collection_name').set_label(frm.doc.customer_collection);
|
||||
}
|
||||
},
|
||||
frequency: function(frm){
|
||||
if(frm.doc.frequency != ''){
|
||||
frm.set_value('start_date', frappe.datetime.get_today());
|
||||
}
|
||||
else{
|
||||
frm.set_value('start_date', '');
|
||||
}
|
||||
},
|
||||
fetch_customers: function(frm){
|
||||
if(frm.doc.collection_name){
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.fetch_customers",
|
||||
args: {
|
||||
'customer_collection': frm.doc.customer_collection,
|
||||
'collection_name': frm.doc.collection_name,
|
||||
'primary_mandatory': frm.doc.primary_mandatory
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
if(r.message.length){
|
||||
frm.clear_table('customers');
|
||||
for (const customer of r.message){
|
||||
var row = frm.add_child('customers');
|
||||
row.customer = customer.name;
|
||||
row.primary_email = customer.primary_email;
|
||||
row.billing_email = customer.billing_email;
|
||||
}
|
||||
frm.refresh_field('customers');
|
||||
}
|
||||
else{
|
||||
frappe.msgprint('No Customers found with selected options.');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
frappe.throw('Enter ' + frm.doc.customer_collection + ' name.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Process Statement Of Accounts Customer', {
|
||||
customer: function(frm, cdt, cdn){
|
||||
var row = locals[cdt][cdn];
|
||||
if (!row.customer){
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails',
|
||||
args: {
|
||||
'customer_name': row.customer,
|
||||
'primary_mandatory': frm.doc.primary_mandatory
|
||||
},
|
||||
callback: function(r){
|
||||
if(!r.exe){
|
||||
if(r.message.length){
|
||||
frappe.model.set_value(cdt, cdn, "primary_email", r.message[0])
|
||||
frappe.model.set_value(cdt, cdn, "billing_email", r.message[1])
|
||||
}
|
||||
else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
@ -0,0 +1,310 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_workflow": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2020-05-22 16:46:18.712954",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"section_break_11",
|
||||
"from_date",
|
||||
"company",
|
||||
"account",
|
||||
"group_by",
|
||||
"cost_center",
|
||||
"column_break_14",
|
||||
"to_date",
|
||||
"finance_book",
|
||||
"currency",
|
||||
"project",
|
||||
"section_break_3",
|
||||
"customer_collection",
|
||||
"collection_name",
|
||||
"fetch_customers",
|
||||
"column_break_6",
|
||||
"primary_mandatory",
|
||||
"column_break_17",
|
||||
"customers",
|
||||
"preferences",
|
||||
"orientation",
|
||||
"section_break_14",
|
||||
"include_ageing",
|
||||
"ageing_based_on",
|
||||
"section_break_1",
|
||||
"enable_auto_email",
|
||||
"section_break_18",
|
||||
"frequency",
|
||||
"filter_duration",
|
||||
"column_break_21",
|
||||
"start_date",
|
||||
"section_break_33",
|
||||
"subject",
|
||||
"column_break_28",
|
||||
"cc_to",
|
||||
"section_break_30",
|
||||
"body",
|
||||
"help_text"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "frequency",
|
||||
"fieldtype": "Select",
|
||||
"label": "Frequency",
|
||||
"options": "Weekly\nMonthly\nQuarterly"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "From Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.enable_auto_email == 0;",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "To Date",
|
||||
"mandatory_depends_on": "eval:doc.frequency == '';"
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Cost Center",
|
||||
"options": "PSOA Cost Center"
|
||||
},
|
||||
{
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"label": "Project",
|
||||
"options": "PSOA Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_3",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Customers"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_11",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "General Ledger Filters"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_14",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_collection",
|
||||
"fieldtype": "Select",
|
||||
"label": "Select Customers By",
|
||||
"options": "\nCustomer Group\nTerritory\nSales Partner\nSales Person"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.customer_collection !== ''",
|
||||
"fieldname": "collection_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Recipient",
|
||||
"options": "customer_collection"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Email Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account",
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "finance_book",
|
||||
"fieldtype": "Link",
|
||||
"label": "Finance Book",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname": "preferences",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Print Preferences"
|
||||
},
|
||||
{
|
||||
"fieldname": "orientation",
|
||||
"fieldtype": "Select",
|
||||
"label": "Orientation",
|
||||
"options": "Landscape\nPortrait"
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "start_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Start Date"
|
||||
},
|
||||
{
|
||||
"default": "Group by Voucher (Consolidated)",
|
||||
"fieldname": "group_by",
|
||||
"fieldtype": "Select",
|
||||
"label": "Group By",
|
||||
"options": "\nGroup by Voucher\nGroup by Voucher (Consolidated)"
|
||||
},
|
||||
{
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "include_ageing",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Include Ageing Summary"
|
||||
},
|
||||
{
|
||||
"default": "Due Date",
|
||||
"depends_on": "eval:doc.include_ageing === 1",
|
||||
"fieldname": "ageing_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Ageing Based On",
|
||||
"options": "Due Date\nPosting Date"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_auto_email",
|
||||
"fieldtype": "Check",
|
||||
"in_list_view": 1,
|
||||
"label": "Enable Auto Email"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Column Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.enable_auto_email ==1",
|
||||
"fieldname": "section_break_18",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_21",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.customer_collection !== ''",
|
||||
"fieldname": "fetch_customers",
|
||||
"fieldtype": "Button",
|
||||
"label": "Fetch Customers",
|
||||
"options": "fetch_customers",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "primary_mandatory",
|
||||
"fieldtype": "Check",
|
||||
"label": "Send To Primary Contact"
|
||||
},
|
||||
{
|
||||
"fieldname": "cc_to",
|
||||
"fieldtype": "Link",
|
||||
"label": "CC To",
|
||||
"options": "User"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "filter_duration",
|
||||
"fieldtype": "Int",
|
||||
"label": "Filter Duration (Months)"
|
||||
},
|
||||
{
|
||||
"fieldname": "customers",
|
||||
"fieldtype": "Table",
|
||||
"label": "Customers",
|
||||
"options": "Process Statement Of Accounts Customer",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_28",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_30",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_33",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "help_text",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Help Text",
|
||||
"options": "<br>\n<h4>Note</h4>\n<ul>\n<li>\nYou can use <a href=\"https://jinja.palletsprojects.com/en/2.11.x/\" target=\"_blank\">Jinja tags</a> in <b>Subject</b> and <b>Body</b> fields for dynamic values.\n</li><li>\n All fields in this doctype are available under the <b>doc</b> object and all fields for the customer to whom the mail will go to is available under the <b>customer</b> object.\n</li></ul>\n<h4> Examples</h4>\n<!-- {% raw %} -->\n<ul>\n <li><b>Subject</b>:<br><br><pre><code>Statement Of Accounts for {{ customer.name }}</code></pre><br></li>\n <li><b>Body</b>: <br><br>\n<pre><code>Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.</code> </pre></li>\n</ul>\n<!-- {% endraw %} -->"
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"label": "Subject"
|
||||
},
|
||||
{
|
||||
"fieldname": "body",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Body"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-08-08 08:47:09.185728",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,271 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, 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
|
||||
from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa
|
||||
from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute as get_ageing
|
||||
from frappe.core.doctype.communication.email import make
|
||||
|
||||
from frappe.utils.print_format import report_to_pdf
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.utils import today, add_days, add_months, getdate, format_date
|
||||
from frappe.utils.jinja import validate_template
|
||||
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
from frappe.www.printview import get_print_style
|
||||
|
||||
class ProcessStatementOfAccounts(Document):
|
||||
def validate(self):
|
||||
if not self.subject:
|
||||
self.subject = 'Statement Of Accounts for {{ customer.name }}'
|
||||
if not self.body:
|
||||
self.body = 'Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.'
|
||||
|
||||
validate_template(self.subject)
|
||||
validate_template(self.body)
|
||||
|
||||
if not self.customers:
|
||||
frappe.throw(frappe._('Customers not selected.'))
|
||||
|
||||
if self.enable_auto_email:
|
||||
self.to_date = self.start_date
|
||||
self.from_date = add_months(self.to_date, -1 * self.filter_duration)
|
||||
|
||||
|
||||
def get_report_pdf(doc, consolidated=True):
|
||||
statement_dict = {}
|
||||
aging = ''
|
||||
base_template_path = "frappe/www/printview.html"
|
||||
template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html"
|
||||
|
||||
for entry in doc.customers:
|
||||
if doc.include_ageing:
|
||||
ageing_filters = frappe._dict({
|
||||
'company': doc.company,
|
||||
'report_date': doc.to_date,
|
||||
'ageing_based_on': doc.ageing_based_on,
|
||||
'range1': 30,
|
||||
'range2': 60,
|
||||
'range3': 90,
|
||||
'range4': 120,
|
||||
'customer': entry.customer
|
||||
})
|
||||
col1, aging = get_ageing(ageing_filters)
|
||||
aging[0]['ageing_based_on'] = doc.ageing_based_on
|
||||
|
||||
tax_id = frappe.get_doc('Customer', entry.customer).tax_id
|
||||
|
||||
filters= frappe._dict({
|
||||
'from_date': doc.from_date,
|
||||
'to_date': doc.to_date,
|
||||
'company': doc.company,
|
||||
'finance_book': doc.finance_book if doc.finance_book else None,
|
||||
"account": doc.account if doc.account else None,
|
||||
'party_type': 'Customer',
|
||||
'party': [entry.customer],
|
||||
'group_by': doc.group_by,
|
||||
'currency': doc.currency,
|
||||
'cost_center': [cc.cost_center_name for cc in doc.cost_center],
|
||||
'project': [p.project_name for p in doc.project],
|
||||
'show_opening_entries': 0,
|
||||
'include_default_book_entries': 0,
|
||||
'show_cancelled_entries': 1,
|
||||
'tax_id': tax_id if tax_id else None
|
||||
})
|
||||
col, res = get_soa(filters)
|
||||
|
||||
for x in [0, -2, -1]:
|
||||
res[x]['account'] = res[x]['account'].replace("'","")
|
||||
|
||||
if len(res) == 3:
|
||||
continue
|
||||
html = frappe.render_template(template_path, \
|
||||
{"filters": filters, "data": res, "aging": aging[0] if doc.include_ageing else None})
|
||||
html = frappe.render_template(base_template_path, {"body": html, \
|
||||
"css": get_print_style(), "title": "Statement For " + entry.customer})
|
||||
statement_dict[entry.customer] = html
|
||||
if not bool(statement_dict):
|
||||
return False
|
||||
elif consolidated:
|
||||
result = ''.join(list(statement_dict.values()))
|
||||
return get_pdf(result, {'orientation': doc.orientation})
|
||||
else:
|
||||
for customer, statement_html in statement_dict.items():
|
||||
statement_dict[customer]=get_pdf(statement_html, {'orientation': doc.orientation})
|
||||
return statement_dict
|
||||
|
||||
def get_customers_based_on_territory_or_customer_group(customer_collection, collection_name):
|
||||
fields_dict = {
|
||||
'Customer Group': 'customer_group',
|
||||
'Territory': 'territory',
|
||||
}
|
||||
collection = frappe.get_doc(customer_collection, collection_name)
|
||||
selected = [customer.name for customer in frappe.get_list(customer_collection, filters=[
|
||||
['lft', '>=', collection.lft],
|
||||
['rgt', '<=', collection.rgt]
|
||||
],
|
||||
fields=['name'],
|
||||
order_by='lft asc, rgt desc'
|
||||
)]
|
||||
return frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[[fields_dict[customer_collection], 'IN', selected]])
|
||||
|
||||
def get_customers_based_on_sales_person(sales_person):
|
||||
lft, rgt = frappe.db.get_value("Sales Person",
|
||||
sales_person, ["lft", "rgt"])
|
||||
records = frappe.db.sql("""
|
||||
select distinct parent, parenttype
|
||||
from `tabSales Team` steam
|
||||
where parenttype = 'Customer'
|
||||
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person)
|
||||
""", (lft, rgt), as_dict=1)
|
||||
sales_person_records = frappe._dict()
|
||||
for d in records:
|
||||
sales_person_records.setdefault(d.parenttype, set()).add(d.parent)
|
||||
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[['name', 'in', list(sales_person_records['Customer'])]])
|
||||
return customers
|
||||
|
||||
def get_recipients_and_cc(customer, doc):
|
||||
recipients = []
|
||||
for clist in doc.customers:
|
||||
if clist.customer == customer:
|
||||
recipients.append(clist.billing_email)
|
||||
if doc.primary_mandatory and clist.primary_email:
|
||||
recipients.append(clist.primary_email)
|
||||
cc = []
|
||||
if doc.cc_to != '':
|
||||
try:
|
||||
cc=[frappe.get_value('User', doc.cc_to, 'email')]
|
||||
except:
|
||||
pass
|
||||
|
||||
return recipients, cc
|
||||
|
||||
def get_context(customer, doc):
|
||||
template_doc = copy.deepcopy(doc)
|
||||
del template_doc.customers
|
||||
template_doc.from_date = format_date(template_doc.from_date)
|
||||
template_doc.to_date = format_date(template_doc.to_date)
|
||||
return {
|
||||
'doc': template_doc,
|
||||
'customer': frappe.get_doc('Customer', customer),
|
||||
'frappe': frappe.utils
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
||||
customer_list = []
|
||||
customers = []
|
||||
|
||||
if customer_collection == 'Sales Person':
|
||||
customers = get_customers_based_on_sales_person(collection_name)
|
||||
if not bool(customers):
|
||||
frappe.throw('No Customers found with selected options.')
|
||||
else:
|
||||
if customer_collection == 'Sales Partner':
|
||||
customers = frappe.get_list('Customer', fields=['name', 'email_id'], \
|
||||
filters=[['default_sales_partner', '=', collection_name]])
|
||||
else:
|
||||
customers = get_customers_based_on_territory_or_customer_group(customer_collection, collection_name)
|
||||
|
||||
for customer in customers:
|
||||
primary_email = customer.get('email_id') or ''
|
||||
billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False)
|
||||
|
||||
if billing_email == '' or (primary_email == '' and int(primary_mandatory)):
|
||||
continue
|
||||
|
||||
customer_list.append({
|
||||
'name': customer.name,
|
||||
'primary_email': primary_email,
|
||||
'billing_email': billing_email
|
||||
})
|
||||
return customer_list
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
|
||||
billing_email = frappe.db.sql("""
|
||||
SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent \
|
||||
WHERE l.link_doctype='Customer' and l.link_name='""" + customer_name + """' and \
|
||||
c.is_billing_contact=1 \
|
||||
order by c.creation desc""")
|
||||
|
||||
if len(billing_email) == 0 or (billing_email[0][0] is None):
|
||||
if billing_and_primary:
|
||||
frappe.throw('No billing email found for customer: '+ customer_name)
|
||||
else:
|
||||
return ''
|
||||
|
||||
if billing_and_primary:
|
||||
primary_email = frappe.get_value('Customer', customer_name, 'email_id')
|
||||
if primary_email is None and int(primary_mandatory):
|
||||
frappe.throw('No primary email found for customer: '+ customer_name)
|
||||
return [primary_email or '', billing_email[0][0]]
|
||||
else:
|
||||
return billing_email[0][0] or ''
|
||||
|
||||
@frappe.whitelist()
|
||||
def download_statements(document_name):
|
||||
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||
report = get_report_pdf(doc)
|
||||
if report:
|
||||
frappe.local.response.filename = doc.name + '.pdf'
|
||||
frappe.local.response.filecontent = report
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_emails(document_name, from_scheduler=False):
|
||||
doc = frappe.get_doc('Process Statement Of Accounts', document_name)
|
||||
report = get_report_pdf(doc, consolidated=False)
|
||||
|
||||
if report:
|
||||
for customer, report_pdf in report.items():
|
||||
attachments = [{
|
||||
'fname': customer + '.pdf',
|
||||
'fcontent': report_pdf
|
||||
}]
|
||||
|
||||
recipients, cc = get_recipients_and_cc(customer, doc)
|
||||
context = get_context(customer, doc)
|
||||
subject = frappe.render_template(doc.subject, context)
|
||||
message = frappe.render_template(doc.body, context)
|
||||
|
||||
frappe.enqueue(
|
||||
queue='short',
|
||||
method=frappe.sendmail,
|
||||
recipients=recipients,
|
||||
sender=frappe.session.user,
|
||||
cc=cc,
|
||||
subject=subject,
|
||||
message=message,
|
||||
now=True,
|
||||
reference_doctype='Process Statement Of Accounts',
|
||||
reference_name=document_name,
|
||||
attachments=attachments
|
||||
)
|
||||
|
||||
if doc.enable_auto_email and from_scheduler:
|
||||
new_to_date = getdate(today())
|
||||
if doc.frequency == 'Weekly':
|
||||
new_to_date = add_days(new_to_date, 7)
|
||||
else:
|
||||
new_to_date = add_months(new_to_date, 1 if doc.frequency == 'Monthly' else 3)
|
||||
new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
|
||||
doc.add_comment('Comment', 'Emails sent on: ' + frappe.utils.format_datetime(frappe.utils.now()))
|
||||
doc.db_set('to_date', new_to_date, commit=True)
|
||||
doc.db_set('from_date', new_from_date, commit=True)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_auto_email():
|
||||
selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
|
||||
for entry in selected:
|
||||
send_emails(entry.name, from_scheduler=True)
|
||||
return True
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestProcessStatementOfAccounts(unittest.TestCase):
|
||||
pass
|
@ -0,0 +1,47 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_workflow": 1,
|
||||
"creation": "2020-08-03 16:35:21.852178",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"customer",
|
||||
"billing_email",
|
||||
"primary_email"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Customer",
|
||||
"options": "Customer",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "primary_email",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Primary Contact Email"
|
||||
},
|
||||
{
|
||||
"fieldname": "billing_email",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Billing Email"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 22:55:38.875601",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts Customer",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, 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 ProcessStatementOfAccountsCustomer(Document):
|
||||
pass
|
@ -0,0 +1,30 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-03 16:56:45.744905",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"cost_center_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "cost_center_name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 16:56:45.744905",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "PSOA Cost Center",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, 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 PSOACostCenter(Document):
|
||||
pass
|
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
0
erpnext/accounts/doctype/psoa_project/__init__.py
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
30
erpnext/accounts/doctype/psoa_project/psoa_project.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2020-08-03 16:52:14.731978",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"project_name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "project_name",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 16:53:39.219736",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "PSOA Project",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
10
erpnext/accounts/doctype/psoa_project/psoa_project.py
Normal file
@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2020, 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 PSOAProject(Document):
|
||||
pass
|
@ -180,7 +180,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "ACC-PINV-.YYYY.-",
|
||||
"options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
@ -1334,7 +1334,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-08-03 12:46:01.411074",
|
||||
"modified": "2020-08-03 23:20:04.466153",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_workflow": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-24 19:29:05",
|
||||
"doctype": "DocType",
|
||||
@ -217,7 +216,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "ACC-SINV-.YYYY.-",
|
||||
"options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
@ -1947,7 +1946,7 @@
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-18 05:07:16.725974",
|
||||
"modified": "2020-08-03 23:31:12.675040",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -206,10 +206,19 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"rate": 14,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Item Quantity",
|
||||
"account_head": "_Test Account Education Cess - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"description": "CESS",
|
||||
"rate": 5,
|
||||
'included_in_print_rate': 1
|
||||
})
|
||||
si.insert()
|
||||
|
||||
# with inclusive tax
|
||||
self.assertEqual(si.net_total, 4385.96)
|
||||
self.assertEqual(si.items[0].net_amount, 3947.368421052631)
|
||||
self.assertEqual(si.net_total, 3947.37)
|
||||
self.assertEqual(si.grand_total, 5000)
|
||||
|
||||
si.reload()
|
||||
@ -222,8 +231,8 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.save()
|
||||
|
||||
# with inclusive tax and additional discount
|
||||
self.assertEqual(si.net_total, 4285.96)
|
||||
self.assertEqual(si.grand_total, 4885.99)
|
||||
self.assertEqual(si.net_total, 3847.37)
|
||||
self.assertEqual(si.grand_total, 4886)
|
||||
|
||||
si.reload()
|
||||
|
||||
@ -235,7 +244,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.save()
|
||||
|
||||
# with inclusive tax and additional discount
|
||||
self.assertEqual(si.net_total, 4298.25)
|
||||
self.assertEqual(si.net_total, 3859.65)
|
||||
self.assertEqual(si.grand_total, 4900.00)
|
||||
|
||||
def test_sales_invoice_discount_amount(self):
|
||||
|
@ -8,7 +8,7 @@ def get_data():
|
||||
'fieldname': 'taxes_and_charges',
|
||||
'non_standard_fieldnames': {
|
||||
'Tax Rule': 'sales_tax_template',
|
||||
'Subscription': 'tax_template',
|
||||
'Subscription': 'sales_tax_template',
|
||||
'Restaurant': 'default_tax_template'
|
||||
},
|
||||
'transactions': [
|
||||
|
@ -611,7 +611,7 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None, futur
|
||||
cond = "posting_date <= '{0}'".format(posting_date)
|
||||
|
||||
if company:
|
||||
cond += "and company = '{0}'".format(company)
|
||||
cond += "and company = {0}".format(frappe.db.escape(company))
|
||||
|
||||
data = frappe.db.sql(""" SELECT party, sum({0}) as amount
|
||||
FROM `tabGL Entry`
|
||||
|
@ -11,7 +11,7 @@ from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate
|
||||
|
||||
class AssetMaintenanceLog(Document):
|
||||
def validate(self):
|
||||
if getdate(self.due_date) < getdate(nowdate()):
|
||||
if getdate(self.due_date) < getdate(nowdate()) and self.maintenance_status not in ["Completed", "Cancelled"]:
|
||||
self.maintenance_status = "Overdue"
|
||||
|
||||
if self.maintenance_status == "Completed" and not self.completion_date:
|
||||
|
@ -1,14 +1,15 @@
|
||||
frappe.listview_settings['Asset Maintenance Log'] = {
|
||||
add_fields: ["maintenance_status"],
|
||||
has_indicator_for_draft: 1,
|
||||
get_indicator: function(doc) {
|
||||
if(doc.maintenance_status=="Pending") {
|
||||
return [__("Pending"), "orange"];
|
||||
} else if(doc.maintenance_status=="Completed") {
|
||||
return [__("Completed"), "green"];
|
||||
} else if(doc.maintenance_status=="Cancelled") {
|
||||
return [__("Cancelled"), "red"];
|
||||
} else if(doc.maintenance_status=="Overdue") {
|
||||
return [__("Overdue"), "red"];
|
||||
if (doc.maintenance_status=="Planned") {
|
||||
return [__(doc.maintenance_status), "orange", "status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status=="Completed") {
|
||||
return [__(doc.maintenance_status), "green", "status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status=="Cancelled") {
|
||||
return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
|
||||
} else if (doc.maintenance_status=="Overdue") {
|
||||
return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ from frappe import _
|
||||
from frappe.utils import flt, getdate, cint, date_diff, formatdate
|
||||
from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
|
||||
from frappe.model.document import Document
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts
|
||||
|
||||
class AssetValueAdjustment(Document):
|
||||
def validate(self):
|
||||
@ -53,17 +54,33 @@ class AssetValueAdjustment(Document):
|
||||
je.company = self.company
|
||||
je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount)
|
||||
|
||||
je.append("accounts", {
|
||||
credit_entry = {
|
||||
"account": accumulated_depreciation_account,
|
||||
"credit_in_account_currency": self.difference_amount,
|
||||
"cost_center": depreciation_cost_center or self.cost_center
|
||||
})
|
||||
}
|
||||
|
||||
je.append("accounts", {
|
||||
debit_entry = {
|
||||
"account": depreciation_expense_account,
|
||||
"debit_in_account_currency": self.difference_amount,
|
||||
"cost_center": depreciation_cost_center or self.cost_center
|
||||
})
|
||||
}
|
||||
|
||||
accounting_dimensions = get_checks_for_pl_and_bs_accounts()
|
||||
|
||||
for dimension in accounting_dimensions:
|
||||
if dimension.get('mandatory_for_bs'):
|
||||
credit_entry.update({
|
||||
dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
|
||||
if dimension.get('mandatory_for_pl'):
|
||||
debit_entry.update({
|
||||
dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension')
|
||||
})
|
||||
|
||||
je.append("accounts", credit_entry)
|
||||
je.append("accounts", debit_entry)
|
||||
|
||||
je.flags.ignore_permissions = True
|
||||
je.submit()
|
||||
|
@ -94,7 +94,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
|
||||
this.frm.add_custom_button(__('Update Items'), () => {
|
||||
erpnext.utils.update_child_items({
|
||||
frm: frm,
|
||||
frm: this.frm,
|
||||
child_docname: "items",
|
||||
child_doctype: "Purchase Order Detail",
|
||||
cannot_add_row: false,
|
||||
|
@ -479,7 +479,11 @@ class AccountsController(TransactionBase):
|
||||
if d.against_order:
|
||||
allocated_amount = flt(d.amount)
|
||||
else:
|
||||
amount = self.rounded_total or self.grand_total
|
||||
if self.get('party_account_currency') == self.company_currency:
|
||||
amount = self.get('base_rounded_total') or self.base_grand_total
|
||||
else:
|
||||
amount = self.get('rounded_total') or self.grand_total
|
||||
|
||||
allocated_amount = min(amount - advance_allocated, d.amount)
|
||||
advance_allocated += flt(allocated_amount)
|
||||
|
||||
@ -802,10 +806,22 @@ class AccountsController(TransactionBase):
|
||||
self.payment_terms_template = ''
|
||||
return
|
||||
|
||||
party_account_currency = self.get('party_account_currency')
|
||||
if not party_account_currency:
|
||||
party_type, party = self.get_party()
|
||||
|
||||
if party_type and party:
|
||||
party_account_currency = get_party_account_currency(party_type, party, self.company)
|
||||
|
||||
posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date")
|
||||
date = self.get("due_date")
|
||||
due_date = date or posting_date
|
||||
grand_total = self.get("rounded_total") or self.grand_total
|
||||
|
||||
if party_account_currency == self.company_currency:
|
||||
grand_total = self.get("base_rounded_total") or self.base_grand_total
|
||||
else:
|
||||
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)
|
||||
|
||||
@ -850,13 +866,25 @@ class AccountsController(TransactionBase):
|
||||
def validate_payment_schedule_amount(self):
|
||||
if self.doctype == 'Sales Invoice' and self.is_pos: return
|
||||
|
||||
party_account_currency = self.get('party_account_currency')
|
||||
if not party_account_currency:
|
||||
party_type, party = self.get_party()
|
||||
|
||||
if party_type and party:
|
||||
party_account_currency = get_party_account_currency(party_type, party, self.company)
|
||||
|
||||
if self.get("payment_schedule"):
|
||||
total = 0
|
||||
for d in self.get("payment_schedule"):
|
||||
total += flt(d.payment_amount)
|
||||
total = flt(total, self.precision("grand_total"))
|
||||
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||
if party_account_currency == self.company_currency:
|
||||
total = flt(total, self.precision("base_grand_total"))
|
||||
grand_total = flt(self.get("base_rounded_total") or self.base_grand_total, self.precision('base_grand_total'))
|
||||
else:
|
||||
total = flt(total, self.precision("grand_total"))
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||
|
||||
if self.get("total_advance"):
|
||||
grand_total -= self.get("total_advance")
|
||||
|
||||
@ -957,7 +985,7 @@ def validate_inclusive_tax(tax, doc):
|
||||
# all rows about the reffered tax should be inclusive
|
||||
_on_previous_row_error("1 - %d" % (tax.row_id,))
|
||||
elif tax.get("category") == "Valuation":
|
||||
frappe.throw(_("Valuation type charges can not marked as Inclusive"))
|
||||
frappe.throw(_("Valuation type charges can not be marked as Inclusive"))
|
||||
|
||||
|
||||
def set_balance_in_account_currency(gl_dict, account_currency=None, conversion_rate=None, company_currency=None):
|
||||
|
@ -613,9 +613,12 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters):
|
||||
if not taxes:
|
||||
return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """)
|
||||
else:
|
||||
valid_from = filters.get('valid_from')
|
||||
valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from
|
||||
|
||||
args = {
|
||||
'item_code': filters.get('item_code'),
|
||||
'posting_date': filters.get('valid_from'),
|
||||
'posting_date': valid_from,
|
||||
'tax_category': filters.get('tax_category'),
|
||||
'company': filters.get('company')
|
||||
}
|
||||
|
@ -161,8 +161,9 @@ class calculate_taxes_and_totals(object):
|
||||
for item in self.doc.get("items"):
|
||||
item_tax_map = self._load_item_tax_rate(item.item_tax_rate)
|
||||
cumulated_tax_fraction = 0
|
||||
total_inclusive_tax_amount_per_qty = 0
|
||||
for i, tax in enumerate(self.doc.get("taxes")):
|
||||
tax.tax_fraction_for_current_item = self.get_current_tax_fraction(tax, item_tax_map)
|
||||
tax.tax_fraction_for_current_item, inclusive_tax_amount_per_qty = self.get_current_tax_fraction(tax, item_tax_map)
|
||||
|
||||
if i==0:
|
||||
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item
|
||||
@ -172,9 +173,12 @@ class calculate_taxes_and_totals(object):
|
||||
+ tax.tax_fraction_for_current_item
|
||||
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item
|
||||
total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.stock_qty)
|
||||
|
||||
if cumulated_tax_fraction and not self.discount_amount_applied and item.qty:
|
||||
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction))
|
||||
if not self.discount_amount_applied and item.qty and (cumulated_tax_fraction or total_inclusive_tax_amount_per_qty):
|
||||
amount = flt(item.amount) - total_inclusive_tax_amount_per_qty
|
||||
|
||||
item.net_amount = flt(amount / (1 + cumulated_tax_fraction))
|
||||
item.net_rate = flt(item.net_amount / item.qty, item.precision("net_rate"))
|
||||
item.discount_percentage = flt(item.discount_percentage,
|
||||
item.precision("discount_percentage"))
|
||||
@ -190,6 +194,7 @@ class calculate_taxes_and_totals(object):
|
||||
from tax inclusive amount
|
||||
"""
|
||||
current_tax_fraction = 0
|
||||
inclusive_tax_amount_per_qty = 0
|
||||
|
||||
if cint(tax.included_in_print_rate):
|
||||
tax_rate = self._get_tax_rate(tax, item_tax_map)
|
||||
@ -205,9 +210,14 @@ class calculate_taxes_and_totals(object):
|
||||
current_tax_fraction = (tax_rate / 100.0) * \
|
||||
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item
|
||||
|
||||
if getattr(tax, "add_deduct_tax", None):
|
||||
current_tax_fraction *= -1.0 if (tax.add_deduct_tax == "Deduct") else 1.0
|
||||
return current_tax_fraction
|
||||
elif tax.charge_type == "On Item Quantity":
|
||||
inclusive_tax_amount_per_qty = flt(tax_rate)
|
||||
|
||||
if getattr(tax, "add_deduct_tax", None) and tax.add_deduct_tax == "Deduct":
|
||||
current_tax_fraction *= -1.0
|
||||
inclusive_tax_amount_per_qty *= -1.0
|
||||
|
||||
return current_tax_fraction, inclusive_tax_amount_per_qty
|
||||
|
||||
def _get_tax_rate(self, tax, item_tax_map):
|
||||
if tax.account_head in item_tax_map:
|
||||
@ -321,7 +331,7 @@ class calculate_taxes_and_totals(object):
|
||||
current_tax_amount = (tax_rate / 100.0) * \
|
||||
self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_for_current_item
|
||||
elif tax.charge_type == "On Item Quantity":
|
||||
current_tax_amount = tax_rate * item.stock_qty
|
||||
current_tax_amount = tax_rate * item.qty
|
||||
|
||||
self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount)
|
||||
|
||||
@ -472,7 +482,7 @@ class calculate_taxes_and_totals(object):
|
||||
actual_taxes_dict = {}
|
||||
|
||||
for tax in self.doc.get("taxes"):
|
||||
if tax.charge_type == "Actual":
|
||||
if tax.charge_type in ["Actual", "On Item Quantity"]:
|
||||
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:
|
||||
|
@ -16,6 +16,7 @@
|
||||
"opportunity_from",
|
||||
"party_name",
|
||||
"customer_name",
|
||||
"source",
|
||||
"column_break0",
|
||||
"title",
|
||||
"opportunity_type",
|
||||
@ -49,10 +50,9 @@
|
||||
"contact_email",
|
||||
"contact_mobile",
|
||||
"more_info",
|
||||
"source",
|
||||
"company",
|
||||
"campaign",
|
||||
"column_break1",
|
||||
"company",
|
||||
"transaction_date",
|
||||
"amended_from",
|
||||
"lost_reasons"
|
||||
@ -344,7 +344,7 @@
|
||||
"collapsible": 1,
|
||||
"fieldname": "more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Source",
|
||||
"label": "More Information",
|
||||
"oldfieldtype": "Section Break",
|
||||
"options": "fa fa-file-text"
|
||||
},
|
||||
@ -424,7 +424,7 @@
|
||||
"icon": "fa fa-info-sign",
|
||||
"idx": 195,
|
||||
"links": [],
|
||||
"modified": "2020-07-14 16:49:15.888503",
|
||||
"modified": "2020-08-11 17:34:35.066961",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Opportunity",
|
||||
|
@ -119,11 +119,19 @@ class Opportunity(TransactionBase):
|
||||
and q.status not in ('Lost', 'Closed')""", self.name)
|
||||
|
||||
def has_ordered_quotation(self):
|
||||
return frappe.db.sql("""
|
||||
select q.name
|
||||
from `tabQuotation` q, `tabQuotation Item` qi
|
||||
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
|
||||
and q.status = 'Ordered'""", self.name)
|
||||
if not self.with_items:
|
||||
return frappe.get_all('Quotation',
|
||||
{
|
||||
'opportunity': self.name,
|
||||
'status': 'Ordered',
|
||||
'docstatus': 1
|
||||
}, 'name')
|
||||
else:
|
||||
return frappe.db.sql("""
|
||||
select q.name
|
||||
from `tabQuotation` q, `tabQuotation Item` qi
|
||||
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
|
||||
and q.status = 'Ordered'""", self.name)
|
||||
|
||||
def has_lost_quotation(self):
|
||||
lost_quotation = frappe.db.sql("""
|
||||
@ -330,7 +338,7 @@ def make_opportunity_from_communication(communication, ignore_communication_link
|
||||
opportunity = frappe.get_doc({
|
||||
"doctype": "Opportunity",
|
||||
"opportunity_from": opportunity_from,
|
||||
"lead": lead
|
||||
"party_name": lead
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links)
|
||||
|
@ -7,7 +7,7 @@ import frappe
|
||||
import unittest
|
||||
from frappe.utils import nowdate
|
||||
from frappe.utils.make_random import get_random
|
||||
|
||||
from erpnext.education.doctype.program.test_program import make_program_and_linked_courses
|
||||
|
||||
# test_records = frappe.get_test_records('Fees')
|
||||
|
||||
@ -15,6 +15,7 @@ class TestFees(unittest.TestCase):
|
||||
|
||||
def test_fees(self):
|
||||
student = get_random("Student")
|
||||
program = make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"])
|
||||
fee = frappe.new_doc("Fees")
|
||||
fee.posting_date = nowdate()
|
||||
fee.due_date = nowdate()
|
||||
@ -23,6 +24,7 @@ class TestFees(unittest.TestCase):
|
||||
fee.income_account = "Sales - _TC"
|
||||
fee.cost_center = "_Test Cost Center - _TC"
|
||||
fee.company = "_Test Company"
|
||||
fee.program = program.name
|
||||
|
||||
fee.extend("components", [
|
||||
{
|
||||
|
@ -134,7 +134,7 @@ let transfer_patient_dialog = function(frm) {
|
||||
{fieldtype: 'Link', label: 'Leave From', fieldname: 'leave_from', options: 'Healthcare Service Unit', reqd: 1, read_only:1},
|
||||
{fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'},
|
||||
{fieldtype: 'Link', label: 'Transfer To', fieldname: 'service_unit', options: 'Healthcare Service Unit', reqd: 1},
|
||||
{fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1}
|
||||
{fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1, default: frappe.datetime.now_datetime()}
|
||||
],
|
||||
primary_action_label: __('Transfer'),
|
||||
primary_action : function() {
|
||||
@ -147,7 +147,12 @@ let transfer_patient_dialog = function(frm) {
|
||||
if(dialog.get_value('service_unit')){
|
||||
service_unit = dialog.get_value('service_unit');
|
||||
}
|
||||
if(!check_in){
|
||||
if(check_in > frappe.datetime.now_datetime()){
|
||||
frappe.msgprint({
|
||||
title: __('Not Allowed'),
|
||||
message: __('Check-in time cannot be greater than the current time'),
|
||||
indicator: 'red'
|
||||
});
|
||||
return;
|
||||
}
|
||||
frappe.call({
|
||||
|
@ -322,7 +322,8 @@ scheduler_events = {
|
||||
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
|
||||
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
|
||||
"erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
|
||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
|
||||
"erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status",
|
||||
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email"
|
||||
],
|
||||
"daily_long": [
|
||||
"erpnext.setup.doctype.email_digest.email_digest.send",
|
||||
|
@ -78,7 +78,7 @@
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "HR",
|
||||
"modified": "2020-06-16 19:20:50.976045",
|
||||
"modified": "2020-08-11 17:04:38.655417",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR",
|
||||
@ -88,7 +88,7 @@
|
||||
"pin_to_top": 0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "#9deca2",
|
||||
"color": "#cef6d1",
|
||||
"format": "{} Active",
|
||||
"label": "Employee",
|
||||
"link_to": "Employee",
|
||||
@ -96,18 +96,19 @@
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Attendance",
|
||||
"link_to": "Attendance",
|
||||
"stats_filter": "",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "#ffe8cd",
|
||||
"format": "{} Open",
|
||||
"label": "Leave Application",
|
||||
"link_to": "Leave Application",
|
||||
"stats_filter": "{\"status\":\"Open\"}",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Attendance",
|
||||
"link_to": "Attendance",
|
||||
"stats_filter": "",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"label": "Job Applicant",
|
||||
"link_to": "Job Applicant",
|
||||
|
@ -8,7 +8,7 @@ frappe.query_reports["Downtime Analysis"] = {
|
||||
label: __("From Date"),
|
||||
fieldname:"from_date",
|
||||
fieldtype: "Datetime",
|
||||
default: frappe.datetime.add_months(frappe.datetime.now_datetime(), -1),
|
||||
default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
|
@ -718,3 +718,5 @@ erpnext.patches.v13_0.delete_report_requested_items_to_order
|
||||
erpnext.patches.v12_0.update_item_tax_template_company
|
||||
erpnext.patches.v13_0.move_branch_code_to_bank_account
|
||||
erpnext.patches.v13_0.healthcare_lab_module_rename_doctypes
|
||||
erpnext.patches.v13_0.stock_entry_enhancements
|
||||
erpnext.patches.v12_0.update_state_code_for_daman_and_diu
|
||||
|
@ -19,7 +19,7 @@ def create_stock_entry_types():
|
||||
|
||||
for purpose in ["Material Issue", "Material Receipt", "Material Transfer",
|
||||
"Material Transfer for Manufacture", "Material Consumption for Manufacture", "Manufacture",
|
||||
"Repack", "Send to Subcontractor", "Send to Warehouse", "Receive at Warehouse"]:
|
||||
"Repack", "Send to Subcontractor"]:
|
||||
|
||||
ste_type = frappe.get_doc({
|
||||
'doctype': 'Stock Entry Type',
|
||||
|
22
erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
Normal file
22
erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py
Normal file
@ -0,0 +1,22 @@
|
||||
import frappe
|
||||
from erpnext.regional.india import states
|
||||
|
||||
def execute():
|
||||
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
# Update options in gst_state custom field
|
||||
gst_state = frappe.get_doc('Custom Field', 'Address-gst_state')
|
||||
gst_state.options = '\n'.join(states)
|
||||
gst_state.save()
|
||||
|
||||
# Update gst_state and state code in existing address
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabAddress`
|
||||
SET
|
||||
gst_state = 'Dadra and Nagar Haveli and Daman and Diu',
|
||||
gst_state_number = 26
|
||||
WHERE gst_state = 'Daman and Diu'
|
||||
""")
|
27
erpnext/patches/v13_0/stock_entry_enhancements.py
Normal file
27
erpnext/patches/v13_0/stock_entry_enhancements.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright(c) 2020, Frappe Technologies Pvt.Ltd.and Contributors
|
||||
# License: GNU General Public License v3.See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "stock_entry")
|
||||
if frappe.db.has_column("Stock Entry", "add_to_transit"):
|
||||
frappe.db.sql("""
|
||||
UPDATE `tabStock Entry` SET
|
||||
stock_entry_type = 'Material Transfer',
|
||||
purpose = 'Material Transfer',
|
||||
add_to_transit = 1 WHERE stock_entry_type = 'Send to Warehouse'
|
||||
""")
|
||||
|
||||
frappe.db.sql("""UPDATE `tabStock Entry` SET
|
||||
stock_entry_type = 'Material Transfer',
|
||||
purpose = 'Material Transfer'
|
||||
WHERE stock_entry_type = 'Receive at Warehouse'
|
||||
""")
|
||||
|
||||
frappe.reload_doc("stock", "doctype", "warehouse_type")
|
||||
if not frappe.db.exists('Warehouse Type', 'Transit'):
|
||||
doc = frappe.new_doc('Warehouse Type')
|
||||
doc.name = 'Transit'
|
||||
doc.insert()
|
@ -8,7 +8,7 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Taxation",
|
||||
"links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]"
|
||||
"links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
@ -38,7 +38,7 @@
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Payroll",
|
||||
"modified": "2020-06-19 12:23:06.034046",
|
||||
"modified": "2020-08-10 19:38:45.976209",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Payroll",
|
||||
"name": "Payroll",
|
||||
|
@ -90,7 +90,7 @@ class PayrollEntry(Document):
|
||||
cond = ''
|
||||
for f in ['company', 'branch', 'department', 'designation']:
|
||||
if self.get(f):
|
||||
cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'"
|
||||
cond += " and t1." + f + " = " + frappe.db.escape(self.get(f))
|
||||
|
||||
return cond
|
||||
|
||||
|
@ -13,7 +13,7 @@ frappe.ui.form.on("Communication", {
|
||||
frappe.confirm(__(confirm_msg, [__("Issue")]), () => {
|
||||
frm.trigger('make_issue_from_communication');
|
||||
})
|
||||
}, "Make");
|
||||
}, "Create");
|
||||
}
|
||||
|
||||
if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) {
|
||||
|
@ -163,9 +163,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
$.each(me.frm.doc["items"] || [], function(n, item) {
|
||||
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
|
||||
var cumulated_tax_fraction = 0.0;
|
||||
|
||||
var total_inclusive_tax_amount_per_qty = 0;
|
||||
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
|
||||
tax.tax_fraction_for_current_item = me.get_current_tax_fraction(tax, item_tax_map);
|
||||
var current_tax_fraction = me.get_current_tax_fraction(tax, item_tax_map);
|
||||
tax.tax_fraction_for_current_item = current_tax_fraction[0];
|
||||
var inclusive_tax_amount_per_qty = current_tax_fraction[1];
|
||||
|
||||
if(i==0) {
|
||||
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
|
||||
@ -176,10 +178,12 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
}
|
||||
|
||||
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
|
||||
total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty);
|
||||
});
|
||||
|
||||
if(cumulated_tax_fraction && !me.discount_amount_applied) {
|
||||
item.net_amount = flt(item.amount / (1 + cumulated_tax_fraction));
|
||||
if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) {
|
||||
var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty;
|
||||
item.net_amount = flt(amount / (1 + cumulated_tax_fraction));
|
||||
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
|
||||
|
||||
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
|
||||
@ -191,6 +195,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
// Get tax fraction for calculating tax exclusive amount
|
||||
// from tax inclusive amount
|
||||
var current_tax_fraction = 0.0;
|
||||
var inclusive_tax_amount_per_qty = 0;
|
||||
|
||||
if(cint(tax.included_in_print_rate)) {
|
||||
var tax_rate = this._get_tax_rate(tax, item_tax_map);
|
||||
@ -205,13 +210,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
} else if(tax.charge_type == "On Previous Row Total") {
|
||||
current_tax_fraction = (tax_rate / 100.0) *
|
||||
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
|
||||
} else if (tax.charge_type == "On Item Quantity") {
|
||||
inclusive_tax_amount_per_qty = flt(tax_rate);
|
||||
}
|
||||
}
|
||||
|
||||
if(tax.add_deduct_tax) {
|
||||
current_tax_fraction *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||
if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
|
||||
current_tax_fraction *= -1;
|
||||
inclusive_tax_amount_per_qty *= -1;
|
||||
}
|
||||
return current_tax_fraction;
|
||||
return [current_tax_fraction, inclusive_tax_amount_per_qty];
|
||||
},
|
||||
|
||||
_get_tax_rate: function(tax, item_tax_map) {
|
||||
@ -360,8 +368,9 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
} else if(tax.charge_type == "On Previous Row Total") {
|
||||
current_tax_amount = (tax_rate / 100.0) *
|
||||
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item;
|
||||
} else if (tax.charge_type == "On Item Quantity") {
|
||||
current_tax_amount = tax_rate * item.qty;
|
||||
}
|
||||
|
||||
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
|
||||
|
||||
return current_tax_amount;
|
||||
@ -573,7 +582,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
var actual_taxes_dict = {};
|
||||
|
||||
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
|
||||
if (tax.charge_type == "Actual") {
|
||||
if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) {
|
||||
var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount;
|
||||
tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
|
||||
actual_taxes_dict[tax.idx] = tax_amount;
|
||||
|
@ -1821,7 +1821,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
set_query_for_item_tax_template: function(doc, cdt, cdn) {
|
||||
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
if(!item.item_code) {
|
||||
frappe.throw(__("Please enter Item Code to get item taxes"));
|
||||
@ -1829,7 +1828,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
|
||||
let filters = {
|
||||
'item_code': item.item_code,
|
||||
'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date,
|
||||
'valid_from': ["<=", doc.transaction_date || doc.bill_date || doc.posting_date],
|
||||
'item_group': item.item_group,
|
||||
}
|
||||
|
||||
|
@ -297,6 +297,10 @@
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.item-group-slideshow {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.product-image-img {
|
||||
border: 1px solid @light-border-color;
|
||||
border-radius: 3px;
|
||||
|
@ -10,8 +10,7 @@ states = [
|
||||
'Bihar',
|
||||
'Chandigarh',
|
||||
'Chhattisgarh',
|
||||
'Dadra and Nagar Haveli',
|
||||
'Daman and Diu',
|
||||
'Dadra and Nagar Haveli and Daman and Diu',
|
||||
'Delhi',
|
||||
'Goa',
|
||||
'Gujarat',
|
||||
@ -50,8 +49,7 @@ state_numbers = {
|
||||
"Bihar": "10",
|
||||
"Chandigarh": "04",
|
||||
"Chhattisgarh": "22",
|
||||
"Dadra and Nagar Haveli": "26",
|
||||
"Daman and Diu": "25",
|
||||
"Dadra and Nagar Haveli and Daman and Diu": "26",
|
||||
"Delhi": "07",
|
||||
"Goa": "30",
|
||||
"Gujarat": "24",
|
||||
|
@ -134,15 +134,10 @@
|
||||
"state_code": "DL",
|
||||
"state_name": "Delhi"
|
||||
},
|
||||
{
|
||||
"state_number": "25",
|
||||
"state_code": "DD",
|
||||
"state_name": "Daman and Diu"
|
||||
},
|
||||
{
|
||||
"state_number": "26",
|
||||
"state_code": "DN",
|
||||
"state_name": "Dadra and Nagar Haveli"
|
||||
"state_name": "Dadra and Nagar Haveli and Daman and Diu"
|
||||
},
|
||||
{
|
||||
"state_number": "22",
|
||||
|
@ -131,6 +131,9 @@ class Gstr1Report(object):
|
||||
taxable_value += abs(net_amount)
|
||||
elif tax_rate:
|
||||
taxable_value += abs(net_amount)
|
||||
elif not tax_rate and self.filters.get('type_of_business') == 'EXPORT' \
|
||||
and invoice_details.get('export_type') == "Without Payment of Tax":
|
||||
taxable_value += abs(net_amount)
|
||||
|
||||
row += [tax_rate or 0, taxable_value]
|
||||
|
||||
|
@ -12,7 +12,8 @@ def get_data():
|
||||
'Payment Entry': 'party',
|
||||
'Quotation': 'party_name',
|
||||
'Opportunity': 'party_name',
|
||||
'Bank Account': 'party'
|
||||
'Bank Account': 'party',
|
||||
'Subscription': 'party'
|
||||
},
|
||||
'dynamic_links': {
|
||||
'party_name': ['Customer', 'quotation_to']
|
||||
|
@ -35,7 +35,8 @@ erpnext.PointOfSale.Controller = class {
|
||||
create_opening_voucher() {
|
||||
const table_fields = [
|
||||
{ fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 },
|
||||
{ fieldname: "opening_amount", fieldtype: "Currency", in_list_view: 1, label: "Opening Amount", options: "company:company_currency", reqd: 1 }
|
||||
{ fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount",
|
||||
options: "company:company_currency", reqd: 1 }
|
||||
];
|
||||
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
@ -66,7 +67,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
frappe.db.get_doc("POS Closing Entry", pos_closing_entry.name).then(({ payment_reconciliation }) => {
|
||||
dialog.fields_dict.balance_details.df.data = [];
|
||||
payment_reconciliation.forEach(pay => {
|
||||
const { mode_of_payment, closing_amount } = pay;
|
||||
const { mode_of_payment } = pay;
|
||||
dialog.fields_dict.balance_details.df.data.push({
|
||||
mode_of_payment: mode_of_payment
|
||||
});
|
||||
@ -152,7 +153,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
},
|
||||
() => this.make_new_invoice(),
|
||||
() => frappe.dom.unfreeze(),
|
||||
() => this.page.set_title(__('Point of Sale Beta')),
|
||||
() => this.page.set_title(__('Point of Sale')),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,16 @@ frappe.ui.form.on("Company", {
|
||||
frm.set_query("default_buying_terms", function() {
|
||||
return { filters: { buying: 1 } };
|
||||
});
|
||||
|
||||
frm.set_query("default_in_transit_warehouse", function() {
|
||||
return {
|
||||
filters:{
|
||||
'warehouse_type' : 'Transit',
|
||||
'is_group': 0,
|
||||
'company': frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
company_name: function(frm) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
"default_selling_terms",
|
||||
"default_buying_terms",
|
||||
"default_warehouse_for_sales_return",
|
||||
"default_in_transit_warehouse",
|
||||
"column_break_10",
|
||||
"country",
|
||||
"create_chart_of_accounts_based_on",
|
||||
@ -242,7 +243,7 @@
|
||||
{
|
||||
"fieldname": "default_warehouse_for_sales_return",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default warehouse for Sales Return",
|
||||
"label": "Default Warehouse for Sales Return",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -733,6 +734,12 @@
|
||||
"fieldname": "enable_perpetual_inventory_for_non_stock_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Perpetual Inventory For Non Stock Items"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_in_transit_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default In Transit Warehouse",
|
||||
"options": "Warehouse"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-building",
|
||||
@ -740,7 +747,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2020-06-24 12:45:31.462195",
|
||||
"modified": "2020-08-06 00:38:08.311216",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
@ -140,7 +140,8 @@ class Company(NestedSet):
|
||||
{"warehouse_name": _("All Warehouses"), "is_group": 1},
|
||||
{"warehouse_name": _("Stores"), "is_group": 0},
|
||||
{"warehouse_name": _("Work In Progress"), "is_group": 0},
|
||||
{"warehouse_name": _("Finished Goods"), "is_group": 0}]:
|
||||
{"warehouse_name": _("Finished Goods"), "is_group": 0},
|
||||
{"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"}]:
|
||||
|
||||
if not frappe.db.exists("Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)):
|
||||
warehouse = frappe.get_doc({
|
||||
@ -149,7 +150,8 @@ class Company(NestedSet):
|
||||
"is_group": wh_detail["is_group"],
|
||||
"company": self.name,
|
||||
"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \
|
||||
if not wh_detail["is_group"] else ""
|
||||
if not wh_detail["is_group"] else "",
|
||||
"warehouse_type" : wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None
|
||||
})
|
||||
warehouse.flags.ignore_permissions = True
|
||||
warehouse.flags.ignore_mandatory = True
|
||||
|
@ -95,8 +95,6 @@ def install(country=None):
|
||||
{'doctype': 'Stock Entry Type', 'name': 'Send to Subcontractor', 'purpose': 'Send to Subcontractor'},
|
||||
{'doctype': 'Stock Entry Type', 'name': 'Material Transfer for Manufacture', 'purpose': 'Material Transfer for Manufacture'},
|
||||
{'doctype': 'Stock Entry Type', 'name': 'Material Consumption for Manufacture', 'purpose': 'Material Consumption for Manufacture'},
|
||||
{'doctype': 'Stock Entry Type', 'name': 'Send to Warehouse', 'purpose': 'Send to Warehouse'},
|
||||
{'doctype': 'Stock Entry Type', 'name': 'Receive at Warehouse', 'purpose': 'Receive at Warehouse'},
|
||||
|
||||
# Designation
|
||||
{'doctype': 'Designation', 'designation_name': _('CEO')},
|
||||
@ -244,7 +242,10 @@ def install(country=None):
|
||||
{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
|
||||
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")}
|
||||
{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
|
||||
|
||||
# Warehouse Type
|
||||
{'doctype': 'Warehouse Type', 'name': 'Transit'},
|
||||
]
|
||||
|
||||
from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
|
||||
|
@ -175,7 +175,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "MAT-DN-.YYYY.-",
|
||||
"options": "MAT-DN-.YYYY.-\nMAT-DN-RET-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
@ -1255,7 +1255,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-18 05:13:55.580420",
|
||||
"modified": "2020-08-03 23:18:47.739997",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -123,6 +123,7 @@
|
||||
"weightage",
|
||||
"slideshow",
|
||||
"website_image",
|
||||
"website_image_alt",
|
||||
"thumbnail",
|
||||
"cb72",
|
||||
"website_warehouse",
|
||||
@ -1054,15 +1055,21 @@
|
||||
"fieldtype": "Data",
|
||||
"label": "Default Manufacturer Part No",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "website_image_alt",
|
||||
"fieldtype": "Data",
|
||||
"label": "Image Description"
|
||||
}
|
||||
],
|
||||
"has_web_view": 1,
|
||||
"icon": "fa fa-tag",
|
||||
"idx": 2,
|
||||
"image_field": "image",
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 1,
|
||||
"modified": "2020-07-31 21:21:10.956453",
|
||||
"modified": "2020-08-07 14:24:58.384992",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
|
@ -343,7 +343,7 @@ class Item(WebsiteGenerator):
|
||||
if variant:
|
||||
context.variant = frappe.get_doc("Item", variant)
|
||||
|
||||
for fieldname in ("website_image", "web_long_description", "description",
|
||||
for fieldname in ("website_image", "website_image_alt", "web_long_description", "description",
|
||||
"website_specifications"):
|
||||
if context.variant.get(fieldname):
|
||||
value = context.variant.get(fieldname)
|
||||
|
@ -11,6 +11,7 @@
|
||||
"naming_series",
|
||||
"title",
|
||||
"material_request_type",
|
||||
"transfer_status",
|
||||
"customer",
|
||||
"column_break_2",
|
||||
"schedule_date",
|
||||
@ -303,13 +304,22 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Set From Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:doc.add_to_transit == 1",
|
||||
"fieldname": "transfer_status",
|
||||
"fieldtype": "Select",
|
||||
"label": "Transfer Status",
|
||||
"options": "\nNot Started\nIn Transit\nCompleted",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-ticket",
|
||||
"idx": 70,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-01 20:21:09.990867",
|
||||
"modified": "2020-08-10 13:27:54.891058",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request",
|
||||
|
@ -1,8 +1,16 @@
|
||||
frappe.listview_settings['Material Request'] = {
|
||||
add_fields: ["material_request_type", "status", "per_ordered", "per_received"],
|
||||
add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status=="Stopped") {
|
||||
return [__("Stopped"), "red", "status,=,Stopped"];
|
||||
} else if(doc.transfer_status && doc.docstatus != 2) {
|
||||
if (doc.transfer_status == "Not Started") {
|
||||
return [__("Not Started"), "orange"];
|
||||
} else if (doc.transfer_status == "In Transit") {
|
||||
return [__("In Transit"), "yellow"];
|
||||
} else if (doc.transfer_status == "Completed") {
|
||||
return [__("Completed"), "green"];
|
||||
}
|
||||
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) {
|
||||
return [__("Pending"), "orange", "per_ordered,=,0"];
|
||||
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_import": 1,
|
||||
"allow_workflow": 1,
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2013-05-21 16:16:39",
|
||||
"doctype": "DocType",
|
||||
@ -160,7 +159,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "naming_series",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "MAT-PRE-.YYYY.-",
|
||||
"options": "MAT-PRE-.YYYY.-\nMAT-PR-RET-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
@ -1110,7 +1109,7 @@
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-18 05:19:12.148115",
|
||||
"modified": "2020-08-03 23:20:26.381024",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
@ -19,7 +19,6 @@ frappe.ui.form.on('Stock Entry', {
|
||||
filters: [
|
||||
['Stock Entry', 'docstatus', '=', 1],
|
||||
['Stock Entry', 'per_transferred', '<','100'],
|
||||
['Stock Entry', 'purpose', '=', 'Send to Warehouse']
|
||||
]
|
||||
}
|
||||
});
|
||||
@ -171,9 +170,9 @@ frappe.ui.form.on('Stock Entry', {
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.purpose == 'Send to Warehouse') {
|
||||
if (frm.doc.per_transferred < 100) {
|
||||
frm.add_custom_button(__('Receive at Warehouse Entry'), function() {
|
||||
if (frm.doc.docstatus === 1) {
|
||||
if (frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer' && frm.doc.per_transferred < 100) {
|
||||
frm.add_custom_button('End Transit', function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.stock.doctype.stock_entry.stock_entry.make_stock_in_entry",
|
||||
frm: frm
|
||||
@ -266,6 +265,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
stock_entry_type: function(frm){
|
||||
frm.remove_custom_button('Bill of Materials', "Get items from");
|
||||
frm.events.show_bom_custom_button(frm);
|
||||
frm.trigger('add_to_transit');
|
||||
},
|
||||
|
||||
purpose: function(frm) {
|
||||
@ -532,6 +532,26 @@ frappe.ui.form.on('Stock Entry', {
|
||||
|
||||
target_warehouse_address: function(frm) {
|
||||
erpnext.utils.get_address_display(frm, 'target_warehouse_address', 'target_address_display', false);
|
||||
},
|
||||
|
||||
add_to_transit: function(frm) {
|
||||
if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
|
||||
frm.set_value('stock_entry_type', 'Material Transfer');
|
||||
frm.fields_dict.to_warehouse.get_query = function() {
|
||||
return {
|
||||
filters:{
|
||||
'warehouse_type' : 'Transit',
|
||||
'is_group': 0,
|
||||
'company': frm.doc.company
|
||||
}
|
||||
};
|
||||
};
|
||||
frappe.db.get_value('Company', frm.doc.company, 'default_in_transit_warehouse', (r) => {
|
||||
if (r.default_in_transit_warehouse) {
|
||||
frm.set_value('to_warehouse', r.default_in_transit_warehouse);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -754,6 +774,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
}
|
||||
erpnext.hide_company();
|
||||
erpnext.utils.add_item(this.frm);
|
||||
this.frm.trigger('add_to_transit');
|
||||
},
|
||||
|
||||
scan_barcode: function() {
|
||||
@ -919,8 +940,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
|
||||
doc.purpose!='Material Issue');
|
||||
|
||||
this.frm.fields_dict["items"].grid.set_column_disp("additional_cost", doc.purpose!='Material Issue');
|
||||
this.frm.toggle_reqd("outgoing_stock_entry",
|
||||
doc.purpose == 'Receive at Warehouse' ? 1: 0);
|
||||
},
|
||||
|
||||
supplier: function(doc) {
|
||||
|
@ -13,6 +13,7 @@
|
||||
"stock_entry_type",
|
||||
"outgoing_stock_entry",
|
||||
"purpose",
|
||||
"add_to_transit",
|
||||
"work_order",
|
||||
"purchase_order",
|
||||
"delivery_note_no",
|
||||
@ -116,11 +117,12 @@
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.purpose == 'Receive at Warehouse'",
|
||||
"depends_on": "eval:doc.purpose == 'Material Transfer'",
|
||||
"fieldname": "outgoing_stock_entry",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock Entry (Outward GIT)",
|
||||
"options": "Stock Entry"
|
||||
"options": "Stock Entry",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
@ -132,7 +134,7 @@
|
||||
"label": "Purpose",
|
||||
"oldfieldname": "purpose",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nSend to Warehouse\nReceive at Warehouse",
|
||||
"options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@ -630,13 +632,21 @@
|
||||
{
|
||||
"fieldname": "print_settings_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.purpose=='Material Transfer' && !doc.outgoing_stock_entry",
|
||||
"fieldname": "add_to_transit",
|
||||
"fieldtype": "Check",
|
||||
"label": "Add to Transit",
|
||||
"no_copy": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-04-23 12:56:52.881752",
|
||||
"modified": "2020-08-11 19:10:07.954981",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry",
|
||||
|
@ -97,6 +97,11 @@ class StockEntry(StockController):
|
||||
if self.work_order and self.purpose == "Manufacture":
|
||||
self.update_so_in_serial_number()
|
||||
|
||||
if self.purpose == 'Material Transfer' and self.add_to_transit:
|
||||
self.set_material_request_transfer_status('In Transit')
|
||||
if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
|
||||
self.set_material_request_transfer_status('Completed')
|
||||
|
||||
def on_cancel(self):
|
||||
|
||||
if self.purchase_order and self.purpose == "Send to Subcontractor":
|
||||
@ -116,6 +121,11 @@ class StockEntry(StockController):
|
||||
self.update_quality_inspection()
|
||||
self.delete_auto_created_batches()
|
||||
|
||||
if self.purpose == 'Material Transfer' and self.add_to_transit:
|
||||
self.set_material_request_transfer_status('Not Started')
|
||||
if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
|
||||
self.set_material_request_transfer_status('In Transit')
|
||||
|
||||
def set_job_card_data(self):
|
||||
if self.job_card and not self.work_order:
|
||||
data = frappe.db.get_value('Job Card',
|
||||
@ -133,7 +143,7 @@ class StockEntry(StockController):
|
||||
def validate_purpose(self):
|
||||
valid_purposes = ["Material Issue", "Material Receipt", "Material Transfer",
|
||||
"Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor",
|
||||
"Material Consumption for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
|
||||
"Material Consumption for Manufacture"]
|
||||
|
||||
if self.purpose not in valid_purposes:
|
||||
frappe.throw(_("Purpose must be one of {0}").format(comma_or(valid_purposes)))
|
||||
@ -199,7 +209,8 @@ class StockEntry(StockController):
|
||||
item.set(f, item_details.get(f))
|
||||
|
||||
if not item.transfer_qty and item.qty:
|
||||
item.transfer_qty = item.qty * item.conversion_factor
|
||||
item.transfer_qty = flt(flt(item.qty) * flt(item.conversion_factor),
|
||||
self.precision("transfer_qty", item))
|
||||
|
||||
if (self.purpose in ("Material Transfer", "Material Transfer for Manufacture")
|
||||
and not item.serial_no
|
||||
@ -258,10 +269,10 @@ class StockEntry(StockController):
|
||||
"""perform various (sometimes conditional) validations on warehouse"""
|
||||
|
||||
source_mandatory = ["Material Issue", "Material Transfer", "Send to Subcontractor", "Material Transfer for Manufacture",
|
||||
"Material Consumption for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
|
||||
"Material Consumption for Manufacture"]
|
||||
|
||||
target_mandatory = ["Material Receipt", "Material Transfer", "Send to Subcontractor",
|
||||
"Material Transfer for Manufacture", "Send to Warehouse", "Receive at Warehouse"]
|
||||
"Material Transfer for Manufacture"]
|
||||
|
||||
validate_for_manufacture = any([d.bom_no for d in self.get("items")])
|
||||
|
||||
@ -809,7 +820,7 @@ class StockEntry(StockController):
|
||||
def set_items_for_stock_in(self):
|
||||
self.items = []
|
||||
|
||||
if self.outgoing_stock_entry and self.purpose == 'Receive at Warehouse':
|
||||
if self.outgoing_stock_entry and self.purpose == 'Material Transfer':
|
||||
doc = frappe.get_doc('Stock Entry', self.outgoing_stock_entry)
|
||||
|
||||
if doc.per_transferred == 100:
|
||||
@ -1210,13 +1221,25 @@ class StockEntry(StockController):
|
||||
|
||||
def validate_with_material_request(self):
|
||||
for item in self.get("items"):
|
||||
if item.material_request:
|
||||
material_request = item.material_request or None
|
||||
material_request_item = item.material_request_item or None
|
||||
if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
|
||||
parent_se = frappe.get_value("Stock Entry Detail", item.ste_detail, ['material_request','material_request_item'],as_dict=True)
|
||||
if parent_se:
|
||||
material_request = parent_se.material_request
|
||||
material_request_item = parent_se.material_request_item
|
||||
|
||||
if material_request:
|
||||
mreq_item = frappe.db.get_value("Material Request Item",
|
||||
{"name": item.material_request_item, "parent": item.material_request},
|
||||
{"name": material_request_item, "parent": material_request},
|
||||
["item_code", "warehouse", "idx"], as_dict=True)
|
||||
if mreq_item.item_code != item.item_code or \
|
||||
mreq_item.warehouse != (item.s_warehouse if self.purpose== "Material Issue" else item.t_warehouse):
|
||||
frappe.throw(_("Item or Warehouse for row {0} does not match Material Request").format(item.idx),
|
||||
if mreq_item.item_code != item.item_code:
|
||||
frappe.throw(_("Item for row {0} does not match Material Request").format(item.idx),
|
||||
frappe.MappingMismatchError)
|
||||
elif self.purpose == "Material Transfer" and self.add_to_transit:
|
||||
continue
|
||||
elif mreq_item.warehouse != (item.s_warehouse if self.purpose == "Material Issue" else item.t_warehouse):
|
||||
frappe.throw(_("Warehouse for row {0} does not match Material Request").format(item.idx),
|
||||
frappe.MappingMismatchError)
|
||||
|
||||
def validate_batch(self):
|
||||
@ -1284,7 +1307,7 @@ class StockEntry(StockController):
|
||||
to fullfill Sales Order {2}.").format(item.item_code, sr, sales_order))
|
||||
|
||||
def update_transferred_qty(self):
|
||||
if self.purpose == 'Receive at Warehouse':
|
||||
if self.purpose == 'Material Transfer' and self.outgoing_stock_entry:
|
||||
stock_entries = {}
|
||||
stock_entries_child_list = []
|
||||
for d in self.items:
|
||||
@ -1342,6 +1365,20 @@ class StockEntry(StockController):
|
||||
'reference_type': reference_type,
|
||||
'reference_name': reference_name
|
||||
})
|
||||
def set_material_request_transfer_status(self, status):
|
||||
material_requests = []
|
||||
if self.outgoing_stock_entry:
|
||||
parent_se = frappe.get_value("Stock Entry", self.outgoing_stock_entry, 'add_to_transit')
|
||||
|
||||
for item in self.items:
|
||||
material_request = item.material_request or None
|
||||
if self.purpose == "Material Transfer" and material_request not in material_requests:
|
||||
if self.outgoing_stock_entry and parent_se:
|
||||
material_request = frappe.get_value("Stock Entry Detail", item.ste_detail, 'material_request')
|
||||
|
||||
if material_request and material_request not in material_requests:
|
||||
material_requests.append(material_request)
|
||||
frappe.db.set_value('Material Request', material_request, 'transfer_status', status)
|
||||
|
||||
@frappe.whitelist()
|
||||
def move_sample_to_retention_warehouse(company, items):
|
||||
@ -1381,12 +1418,19 @@ def move_sample_to_retention_warehouse(company, items):
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_stock_in_entry(source_name, target_doc=None):
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.purpose = 'Receive at Warehouse'
|
||||
target.set_stock_entry_type()
|
||||
|
||||
def update_item(source_doc, target_doc, source_parent):
|
||||
target_doc.t_warehouse = ''
|
||||
|
||||
if source_doc.material_request_item and source_doc.material_request :
|
||||
add_to_transit = frappe.db.get_value('Stock Entry', source_name, 'add_to_transit')
|
||||
if add_to_transit:
|
||||
warehouse = frappe.get_value('Material Request Item', source_doc.material_request_item, 'warehouse')
|
||||
target_doc.t_warehouse = warehouse
|
||||
|
||||
target_doc.s_warehouse = source_doc.t_warehouse
|
||||
target_doc.qty = source_doc.qty - source_doc.transferred_qty
|
||||
|
||||
|
@ -737,34 +737,6 @@ class TestStockEntry(unittest.TestCase):
|
||||
self.assertEqual(se.get("items")[0].allow_zero_valuation_rate, 1)
|
||||
self.assertEqual(se.get("items")[0].amount, 0)
|
||||
|
||||
def test_goods_in_transit(self):
|
||||
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
|
||||
warehouse = "_Test Warehouse FG 1 - _TC"
|
||||
|
||||
if not frappe.db.exists('Warehouse', warehouse):
|
||||
create_warehouse("_Test Warehouse FG 1")
|
||||
|
||||
outward_entry = make_stock_entry(item_code="_Test Item",
|
||||
purpose="Send to Warehouse",
|
||||
source="_Test Warehouse - _TC",
|
||||
target="_Test Warehouse 1 - _TC", qty=50, basic_rate=100)
|
||||
|
||||
inward_entry1 = make_stock_in_entry(outward_entry.name)
|
||||
inward_entry1.items[0].t_warehouse = warehouse
|
||||
inward_entry1.items[0].qty = 25
|
||||
inward_entry1.submit()
|
||||
|
||||
doc = frappe.get_doc('Stock Entry', outward_entry.name)
|
||||
self.assertEqual(doc.per_transferred, 50)
|
||||
|
||||
inward_entry2 = make_stock_in_entry(outward_entry.name)
|
||||
inward_entry2.items[0].t_warehouse = warehouse
|
||||
inward_entry2.items[0].qty = 25
|
||||
inward_entry2.submit()
|
||||
|
||||
doc = frappe.get_doc('Stock Entry', outward_entry.name)
|
||||
self.assertEqual(doc.per_transferred, 100)
|
||||
|
||||
def test_gle_for_opening_stock_entry(self):
|
||||
mr = make_stock_entry(item_code="_Test Item", target="Stores - TCP1", company="_Test Company with perpetual inventory",qty=50, basic_rate=100, expense_account="Stock Adjustment - TCP1", is_opening="Yes", do_not_save=True)
|
||||
|
||||
|
@ -1,156 +1,83 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"actions": [],
|
||||
"autoname": "Prompt",
|
||||
"beta": 0,
|
||||
"creation": "2019-03-13 16:23:46.636769",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"purpose"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Material Issue",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "purpose",
|
||||
"fieldtype": "Select",
|
||||
"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": "Purpose",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor\nSend to Warehouse\nReceive at Warehouse",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"set_only_once": 1
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-26 12:02:42.144377",
|
||||
"links": [],
|
||||
"modified": "2020-08-10 23:24:37.160817",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Entry Type",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Stock Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Stock User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
@ -23,7 +23,7 @@
|
||||
})
|
||||
</script>
|
||||
{% else %}
|
||||
{{ product_image(website_image or image or 'no-image.jpg') }}
|
||||
{{ product_image(website_image or image or 'no-image.jpg', alt=website_image_alt or item_name) }}
|
||||
{% endif %}
|
||||
|
||||
<!-- Simple image preview -->
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div class="item-group-content" itemscope itemtype="http://schema.org/Product">
|
||||
<div>
|
||||
<div class="item-group-slideshow">
|
||||
{% if slideshow %}<!-- slideshow -->
|
||||
{% include "templates/includes/slideshow.html" %}
|
||||
{% endif %}
|
||||
|
@ -7,9 +7,9 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro product_image(website_image, css_class="") %}
|
||||
{% macro product_image(website_image, css_class="", alt="") %}
|
||||
<div class="border text-center rounded h-100 {{ css_class }}" style="overflow: hidden;">
|
||||
<img itemprop="image" class="website-image h-100 w-100" src="{{ frappe.utils.quoted(website_image or 'no-image.jpg') | abs_url }}">
|
||||
<img itemprop="image" class="website-image h-100 w-100" alt="{{ alt }}" src="{{ frappe.utils.quoted(website_image or 'no-image.jpg') | abs_url }}">
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user