mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-28 06:58:17 +00:00
Compare commits
177 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1098755d26 | ||
|
|
774f676a25 | ||
|
|
1629393618 | ||
|
|
4cf46edabc | ||
|
|
e6498996fa | ||
|
|
98b7bb5679 | ||
|
|
99017b173d | ||
|
|
42f2d1a7a8 | ||
|
|
a125a6199c | ||
|
|
ba1cdc6c57 | ||
|
|
875c941f3d | ||
|
|
8eeb5694ba | ||
|
|
0bef91c241 | ||
|
|
ae02209b6a | ||
|
|
e0adb6bbd6 | ||
|
|
efa191c72e | ||
|
|
40fe3a2cdf | ||
|
|
ed77429496 | ||
|
|
c7afbea708 | ||
|
|
dd8fba6783 | ||
|
|
8e74df09a4 | ||
|
|
3d73a6f7f2 | ||
|
|
46c8ccc6a8 | ||
|
|
5d0674a23a | ||
|
|
a831868d02 | ||
|
|
943dc09f29 | ||
|
|
ea7533fa75 | ||
|
|
b9399d8781 | ||
|
|
8cd0f67b25 | ||
|
|
32a118d68c | ||
|
|
a02d5d1104 | ||
|
|
36cd433999 | ||
|
|
6ff3a094e2 | ||
|
|
50d8c4a10f | ||
|
|
7c088ff623 | ||
|
|
7cf01e7c75 | ||
|
|
fc03a0463c | ||
|
|
5ecc26bf15 | ||
|
|
c1cf495a93 | ||
|
|
ce642d73cb | ||
|
|
2fa057e4ad | ||
|
|
7e0e85912b | ||
|
|
defecd3785 | ||
|
|
fccf7f2a68 | ||
|
|
cce7189b4f | ||
|
|
31a96d2a3f | ||
|
|
39c0add5a2 | ||
|
|
58200182b4 | ||
|
|
c2c73ae96a | ||
|
|
ae95a8aa15 | ||
|
|
b9f54ca946 | ||
|
|
a8c6ec27c0 | ||
|
|
3c1b153cdb | ||
|
|
06f91e2dc1 | ||
|
|
13f39eb821 | ||
|
|
a8fb2db001 | ||
|
|
27cf190269 | ||
|
|
7ad556cd4c | ||
|
|
475729cefe | ||
|
|
f7713ebfa7 | ||
|
|
92640cc899 | ||
|
|
826f020862 | ||
|
|
93a186aa11 | ||
|
|
742d3e5549 | ||
|
|
bcbe32ce7c | ||
|
|
b434f2687e | ||
|
|
a752eca82c | ||
|
|
37da7d7d05 | ||
|
|
b5d9b38dee | ||
|
|
11e2a9ac90 | ||
|
|
1275ea5123 | ||
|
|
812224e56e | ||
|
|
8f17281011 | ||
|
|
8233cb11ed | ||
|
|
984a7a7e61 | ||
|
|
b5cc946771 | ||
|
|
65dd1f6b4c | ||
|
|
7c9a6eba3f | ||
|
|
f2ccde0454 | ||
|
|
9e4889d756 | ||
|
|
b81ece9ddf | ||
|
|
2f822476dd | ||
|
|
dcfe19d868 | ||
|
|
4f40b2dca5 | ||
|
|
1b48fde986 | ||
|
|
33db332eca | ||
|
|
257e18b640 | ||
|
|
292f46fec0 | ||
|
|
78a0be666f | ||
|
|
ce4f520908 | ||
|
|
29160441bb | ||
|
|
1b18bba04a | ||
|
|
f8c26bb778 | ||
|
|
ccd0617e19 | ||
|
|
bf10d0f98b | ||
|
|
dbb67fb4fd | ||
|
|
404f39d373 | ||
|
|
bbee9b6cc4 | ||
|
|
69b0535e10 | ||
|
|
7f63c1ad7c | ||
|
|
ebe1ebead2 | ||
|
|
f2b3307136 | ||
|
|
9d215c2d9b | ||
|
|
3cf0d51e65 | ||
|
|
9fc5ddc26e | ||
|
|
0ff31f0ebf | ||
|
|
40349f4b00 | ||
|
|
b36517158b | ||
|
|
3b9720f0be | ||
|
|
cf26b2ca1c | ||
|
|
02a40e9d7b | ||
|
|
ea199f9cd9 | ||
|
|
161f35a687 | ||
|
|
fcb984b294 | ||
|
|
723fe8fb1b | ||
|
|
47c0c8ba49 | ||
|
|
3e0a937cb8 | ||
|
|
bd63f0056a | ||
|
|
55daa9cd24 | ||
|
|
764b9bfeba | ||
|
|
62b985d405 | ||
|
|
ed393d1025 | ||
|
|
ed376cacc8 | ||
|
|
7461806b1c | ||
|
|
bb7c5ac0f8 | ||
|
|
0272fc05bf | ||
|
|
4f6b68fef2 | ||
|
|
eae2ddac69 | ||
|
|
99be9d17d5 | ||
|
|
5df63e36d4 | ||
|
|
bd99606a14 | ||
|
|
b0a46f397a | ||
|
|
53c9b63c0b | ||
|
|
9b2078feab | ||
|
|
30304e68ff | ||
|
|
6bc500bffa | ||
|
|
8be895091e | ||
|
|
f5f8a1f288 | ||
|
|
c925a38f79 | ||
|
|
c23230faea | ||
|
|
6855b87f76 | ||
|
|
07d030208f | ||
|
|
47f6e32920 | ||
|
|
7bd1453f8f | ||
|
|
bf416cfbf8 | ||
|
|
67918fff32 | ||
|
|
132dac9712 | ||
|
|
57914f140e | ||
|
|
ede0af97cd | ||
|
|
ad4ede0e23 | ||
|
|
cfcf9b3832 | ||
|
|
d2065ce3bf | ||
|
|
31edddace6 | ||
|
|
8f29159fb7 | ||
|
|
d9f1b539dc | ||
|
|
60bd383a25 | ||
|
|
c0ec3c3f7b | ||
|
|
60aa1170d5 | ||
|
|
9630aa3d7f | ||
|
|
aa6576c0ea | ||
|
|
fa5ecb066e | ||
|
|
c36524ec54 | ||
|
|
cba3570cf6 | ||
|
|
5b7503c074 | ||
|
|
8441dd9184 | ||
|
|
78869f1e77 | ||
|
|
ff8f1bc88e | ||
|
|
91b0dce4d8 | ||
|
|
59f7b8c4a1 | ||
|
|
2476a8aab9 | ||
|
|
46ffbb74ff | ||
|
|
492aff2a8b | ||
|
|
c3fbc04aab | ||
|
|
5b866db6bc | ||
|
|
9fbb8192e1 | ||
|
|
ea1408f2e0 | ||
|
|
f2a941b1a4 |
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
68
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
Issue: Bug report
|
||||
|
||||
Our project, as you've probably heard, is getting really popular and truth is we're getting a bit overwhelmed by the activity surrounding it. There are just too many issues for us to manage properly.
|
||||
|
||||
**Do the checklist before filing an issue:**
|
||||
- [ ] Have a usage question? Ask your question on [Discuss Forum](https://discuss.erpnext.com). We use [Discuss Forum](https://discuss.erpnext.com) for usage question and GitHub for bugs.
|
||||
- [ ] Can you replicate the issue?
|
||||
- [ ] Is this something you can debug and fix? Send a pull request! Bug fixes and documentation fixes are welcome
|
||||
|
||||
**Describe the bug** :chart_with_downwards_trend:
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce** :page_with_curl:
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior** :chart_with_upwards_trend:
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots** :crystal_ball:
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Desktop (please complete the following information):** :cyclone:
|
||||
- OS:
|
||||
- [ ] Linux
|
||||
- [ ] macOS
|
||||
- [ ] Windows
|
||||
- [ ] Others? Please mention:
|
||||
- Browser:
|
||||
- [ ] Safari
|
||||
- [ ] Chrome
|
||||
- [ ] Firefox
|
||||
- [ ] Other? Please mention:
|
||||
|
||||
**Smartphone (please complete the following information):** :iphone: :computer:
|
||||
- Device:
|
||||
- [ ] iPhone
|
||||
- [ ] Android
|
||||
- Browser:
|
||||
- [ ] Safari
|
||||
- [ ] Chrome
|
||||
- [ ] Firefox
|
||||
- [ ] Other? Please mention:
|
||||
|
||||
**Version Information**
|
||||
- Which branch are you on?
|
||||
- [ ] `master` :star2:
|
||||
- [ ] `develop` :fire:
|
||||
- Frappe Version:
|
||||
- ERPNext Version:
|
||||
|
||||
**Additional context** :page_facing_up:
|
||||
Add any other context about the problem here.
|
||||
|
||||
**Possible Solution** :bookmark_tabs:
|
||||
Any idea what might be causing the issue. Or if you have a proposed solution to the problem,
|
||||
|
||||
**Please don't be intimidated by the long list of options you've fill. Try to fill out as much as you can. Remember, the more the information the easier it is for us to replicate and fix the issue** :grin:
|
||||
|
||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
Issue: Feature Request
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
|
||||
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
28
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
Pull-Request
|
||||
|
||||
- [ ] Have you followed the guidelines in our Contributing document?
|
||||
- [ ] Have you checked to ensure there aren't other open [Pull Requests](../pulls) for the same update/change?
|
||||
- [ ] Have you lint your code locally prior to submission?
|
||||
- [ ] Have you successfully run tests with your changes locally?
|
||||
- [ ] Does your commit message have an explanation for your changes and why you'd like us to include them?
|
||||
- [ ] Docs have been added / updated
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Did you modify the existing test cases? If yes, why?
|
||||
|
||||
---
|
||||
|
||||
What type of a PR is this?
|
||||
|
||||
- [ ] Changes to Existing Features
|
||||
- [ ] New Feature Submissions
|
||||
- [ ] Bug Fix
|
||||
- [ ] Breaking Change
|
||||
|
||||
---
|
||||
|
||||
- Motivation and Context (What existing problem does the pull request solve):
|
||||
- Related Issue:
|
||||
- Screenshots (if applicable, remember, a picture tells a thousand words):
|
||||
|
||||
**Please don't be intimidated by the long list of options you've fill. Try to fill out as much as you can. Remember, the more the information the easier it is for us to test and get your pull request merged** :grin:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@ latest_updates.json
|
||||
*.egg-info
|
||||
dist/
|
||||
erpnext/docs/current
|
||||
*.swp
|
||||
|
||||
@@ -11,6 +11,7 @@ install:
|
||||
- pip install flake8==3.3.0
|
||||
- flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics
|
||||
- sudo rm /etc/apt/sources.list.d/docker.list
|
||||
- sudo apt-get install hhvm && rm -rf /home/travis/.kiex/
|
||||
- sudo apt-get purge -y mysql-common mysql-server mysql-client
|
||||
- nvm install v7.10.0
|
||||
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py
|
||||
@@ -32,6 +33,7 @@ before_script:
|
||||
- bench reinstall --yes
|
||||
- bench build
|
||||
- bench scheduler disable
|
||||
- sed -i 's/9000/9001/g' sites/common_site_config.json
|
||||
- bench start &
|
||||
- sleep 10
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '10.1.22'
|
||||
__version__ = '10.1.40'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -71,7 +71,7 @@ frappe.treeview_settings["Account"] = {
|
||||
|
||||
// financial statements
|
||||
for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
|
||||
'Profit and Loss', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||
'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||
treeview.page.add_inner_button(__(report), function() {
|
||||
frappe.set_route('query-report', report, {company: get_company()});
|
||||
}, __('Financial Statements'));
|
||||
|
||||
@@ -165,7 +165,7 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name] = {}
|
||||
|
||||
# assign account_type and root_type
|
||||
if child.account_type:
|
||||
if child.account_number:
|
||||
tree[child.account_name]["account_number"] = child.account_number
|
||||
if child.account_type:
|
||||
tree[child.account_name]["account_type"] = child.account_type
|
||||
@@ -175,4 +175,4 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name]["root_type"] = child.root_type
|
||||
|
||||
# call recursively to build a subtree for current account
|
||||
build_account_tree(tree[child.account_name], child, all_accounts)
|
||||
build_account_tree(tree[child.account_name], child, all_accounts)
|
||||
|
||||
@@ -27,22 +27,32 @@ def create_or_update_cheque_print_format(template_name):
|
||||
doc = frappe.get_doc("Cheque Print Template", template_name)
|
||||
|
||||
cheque_print.html = """
|
||||
<style>
|
||||
.print-format {
|
||||
padding: 0px;
|
||||
}
|
||||
@media screen {
|
||||
.print-format {
|
||||
padding: 0in;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div style="position: relative; top:%(starting_position_from_top_edge)scm">
|
||||
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;">
|
||||
<span style="top: {{ %(acc_pay_dist_from_top_edge)s }}cm; left: {{ %(acc_pay_dist_from_left_edge)s }}cm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; position: absolute;">
|
||||
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm;
|
||||
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
|
||||
%(message_to_show)s
|
||||
</span>
|
||||
<span style="top:%(date_dist_from_top_edge)s cm; left:%(date_dist_from_left_edge)scm;
|
||||
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
{{ frappe.utils.formatdate(doc.reference_date) or '' }}
|
||||
</span>
|
||||
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{ doc.account_no or '' }}
|
||||
</span>
|
||||
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.party_name}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm;
|
||||
@@ -51,11 +61,11 @@ def create_or_update_cheque_print_format(template_name):
|
||||
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}
|
||||
</span>
|
||||
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 4cm;">
|
||||
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}
|
||||
</span>
|
||||
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm;
|
||||
position: absolute;">
|
||||
position: absolute; min-width: 6cm;">
|
||||
{{doc.company}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -82,7 +82,7 @@ class JournalEntry(AccountsController):
|
||||
d.reference_type = ''
|
||||
d.reference_name = ''
|
||||
d.db_update()
|
||||
|
||||
|
||||
def unlink_asset_reference(self):
|
||||
for d in self.get("accounts"):
|
||||
if d.reference_type=="Asset" and d.reference_name:
|
||||
@@ -125,7 +125,7 @@ class JournalEntry(AccountsController):
|
||||
if (d.party_type == 'Customer' and flt(d.credit) > 0) or \
|
||||
(d.party_type == 'Supplier' and flt(d.debit) > 0):
|
||||
if d.is_advance=="No":
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account))
|
||||
msgprint(_("Row {0}: Please check 'Is Advance' against Account {1} if this is an advance entry.").format(d.idx, d.account), alert=1)
|
||||
elif d.reference_type in ("Sales Order", "Purchase Order") and d.is_advance != "Yes":
|
||||
frappe.throw(_("Row {0}: Payment against Sales/Purchase Order should always be marked as advance").format(d.idx))
|
||||
|
||||
|
||||
@@ -138,7 +138,8 @@ class OpeningInvoiceCreationTool(Document):
|
||||
income_expense_account_field = "expense_account"
|
||||
|
||||
item = get_item_dict()
|
||||
return frappe._dict({
|
||||
|
||||
args = frappe._dict({
|
||||
"items": [item],
|
||||
"is_opening": "Yes",
|
||||
"set_posting_time": 1,
|
||||
@@ -150,6 +151,11 @@ class OpeningInvoiceCreationTool(Document):
|
||||
"currency": frappe.db.get_value("Company", self.company, "default_currency")
|
||||
})
|
||||
|
||||
if self.invoice_type == "Sales":
|
||||
args["is_pos"] = 0
|
||||
|
||||
return args
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_temporary_opening_account(company=None):
|
||||
if not company:
|
||||
|
||||
@@ -588,6 +588,10 @@ frappe.ui.form.on('Payment Entry', {
|
||||
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
|
||||
var total_positive_outstanding_including_order = 0;
|
||||
var total_negative_outstanding = 0;
|
||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||
function(d) { return flt(d.amount) }));
|
||||
|
||||
paid_amount -= total_deductions;
|
||||
|
||||
$.each(frm.doc.references || [], function(i, row) {
|
||||
if(flt(row.outstanding_amount) > 0)
|
||||
@@ -828,6 +832,11 @@ frappe.ui.form.on('Payment Entry Reference', {
|
||||
$.each(r.message, function(field, value) {
|
||||
frappe.model.set_value(cdt, cdn, field, value);
|
||||
})
|
||||
|
||||
let allocated_amount = frm.doc.unallocated_amount > row.outstanding_amount ?
|
||||
row.outstanding_amount : frm.doc.unallocated_amount;
|
||||
|
||||
frappe.model.set_value(cdt, cdn, 'allocated_amount', allocated_amount);
|
||||
frm.refresh_fields();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,14 +337,15 @@ class PaymentEntry(AccountsController):
|
||||
total_negative_outstanding = sum([abs(flt(d.outstanding_amount))
|
||||
for d in self.get("references") if flt(d.outstanding_amount) < 0])
|
||||
|
||||
party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
|
||||
paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
|
||||
additional_charges = sum([flt(d.amount) for d in self.deductions])
|
||||
|
||||
if not total_negative_outstanding:
|
||||
frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
|
||||
.format(self.payment_type, ("to" if self.party_type=="Customer" else "from"),
|
||||
self.party_type), InvalidPaymentEntry)
|
||||
|
||||
elif party_amount > total_negative_outstanding:
|
||||
elif paid_amount - additional_charges > total_negative_outstanding:
|
||||
frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
|
||||
.format(total_negative_outstanding), InvalidPaymentEntry)
|
||||
|
||||
@@ -724,8 +725,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
|
||||
exchange_rate = ref_doc.get("conversion_rate") or \
|
||||
get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
|
||||
|
||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
|
||||
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
outstanding_amount = ref_doc.get("outstanding_amount")
|
||||
elif reference_doctype == "Expense Claim":
|
||||
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) \
|
||||
- flt(ref_doc.get("total_amount+reimbursed")) - flt(ref_doc.get("total_advance_amount"))
|
||||
elif reference_doctype == "Employee Advance":
|
||||
outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount)
|
||||
else:
|
||||
|
||||
@@ -111,8 +111,8 @@ def apply_pricing_rule(args):
|
||||
|
||||
item_list = args.get("items")
|
||||
args.pop("items")
|
||||
|
||||
set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
|
||||
|
||||
set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
|
||||
"automatically_set_serial_nos_based_on_fifo")
|
||||
|
||||
for item in item_list:
|
||||
@@ -122,7 +122,7 @@ def apply_pricing_rule(args):
|
||||
if set_serial_nos_based_on_fifo and not args.get('is_return'):
|
||||
out.append(get_serial_no_for_item(args_copy))
|
||||
return out
|
||||
|
||||
|
||||
def get_serial_no_for_item(args):
|
||||
from erpnext.stock.get_item_details import get_serial_no
|
||||
|
||||
@@ -143,7 +143,7 @@ def get_pricing_rule_for_item(args):
|
||||
"name": args.name,
|
||||
"pricing_rule": None
|
||||
})
|
||||
|
||||
|
||||
if args.ignore_pricing_rule or not args.item_code:
|
||||
if frappe.db.exists(args.doctype, args.name) and args.get("pricing_rule"):
|
||||
item_details = remove_pricing_rule_for_item(args.get("pricing_rule"), item_details)
|
||||
@@ -180,7 +180,7 @@ def get_pricing_rule_for_item(args):
|
||||
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
|
||||
if pricing_rule.price_or_discount == "Price":
|
||||
item_details.update({
|
||||
"price_list_rate": (pricing_rule.price/flt(args.conversion_rate)) * args.conversion_factor or 1.0 \
|
||||
"price_list_rate": (pricing_rule.price/flt(args.conversion_rate)) * (args.conversion_factor or 1.0) \
|
||||
if args.conversion_rate else 0.0,
|
||||
"discount_percentage": 0.0
|
||||
})
|
||||
@@ -192,7 +192,7 @@ def get_pricing_rule_for_item(args):
|
||||
return item_details
|
||||
|
||||
def remove_pricing_rule_for_item(pricing_rule, item_details):
|
||||
pricing_rule = frappe.db.get_value('Pricing Rule', pricing_rule,
|
||||
pricing_rule = frappe.db.get_value('Pricing Rule', pricing_rule,
|
||||
['price_or_discount', 'margin_type'], as_dict=1)
|
||||
if pricing_rule and pricing_rule.price_or_discount == 'Discount Percentage':
|
||||
item_details.discount_percentage = 0.0
|
||||
@@ -209,14 +209,14 @@ def remove_pricing_rule_for_item(pricing_rule, item_details):
|
||||
def remove_pricing_rules(item_list):
|
||||
if isinstance(item_list, basestring):
|
||||
item_list = json.loads(item_list)
|
||||
|
||||
out = []
|
||||
|
||||
out = []
|
||||
for item in item_list:
|
||||
item = frappe._dict(item)
|
||||
out.append(remove_pricing_rule_for_item(item.get("pricing_rule"), item))
|
||||
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def get_pricing_rules(args):
|
||||
def _get_tree_conditions(parenttype, allow_blank=True):
|
||||
field = frappe.scrub(parenttype)
|
||||
|
||||
@@ -3410,6 +3410,65 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_112",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_print_heading",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -3914,7 +3973,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-12-20 17:49:51.230092",
|
||||
"modified": "2018-05-28 02:38:40.310899",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -319,6 +319,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
},
|
||||
|
||||
pos_profile: function() {
|
||||
this.frm.doc.taxes = []
|
||||
this.set_pos_data();
|
||||
},
|
||||
|
||||
|
||||
@@ -3742,12 +3742,12 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "language",
|
||||
"fieldtype": "Data",
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -3755,14 +3755,14 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Language",
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -3831,6 +3831,36 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "language",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Language",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -4683,7 +4713,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2018-03-16 15:19:54.711885",
|
||||
"modified": "2018-05-28 04:47:29.879475",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -39,18 +39,20 @@ frappe.ui.form.on("Tax Rule", "customer", function(frm) {
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Tax Rule", "supplier", function(frm) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.supplier,
|
||||
"party_type": "supplier"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
if(frm.doc.supplier) {
|
||||
frappe.call({
|
||||
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
|
||||
args: {
|
||||
"party": frm.doc.supplier,
|
||||
"party_type": "supplier"
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
frm.set_value(k, v);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -404,10 +404,21 @@ def get_timeline_data(doctype, name):
|
||||
from frappe.desk.form.load import get_communication_data
|
||||
|
||||
out = {}
|
||||
fields = 'date(creation), count(name)'
|
||||
after = add_years(None, -1).strftime('%Y-%m-%d')
|
||||
group_by='group by date(creation)'
|
||||
|
||||
data = get_communication_data(doctype, name,
|
||||
fields = 'date(creation), count(name)',
|
||||
after = add_years(None, -1).strftime('%Y-%m-%d'),
|
||||
group_by='group by date(creation)', as_dict=False)
|
||||
fields=fields, after=after, group_by=group_by, as_dict=False)
|
||||
|
||||
# fetch and append data from Activity Log
|
||||
data += frappe.db.sql("""select {fields}
|
||||
from `tabActivity Log`
|
||||
where reference_doctype="{doctype}" and reference_name="{name}"
|
||||
and status!='Success' and creation > {after}
|
||||
{group_by} order by creation desc
|
||||
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
|
||||
group_by=group_by, after=after), as_dict=False)
|
||||
|
||||
timeline_items = dict(data)
|
||||
|
||||
|
||||
@@ -19,7 +19,13 @@
|
||||
|
||||
|
||||
<h2 class="text-center">{%= __(report.report_name) %}</h2>
|
||||
<h4 class="text-center">{%= filters.customer || filters.supplier %} </h4>
|
||||
<h4 class="text-center">
|
||||
{% if (filters.customer_name) { %}
|
||||
{%= filters.customer_name %}
|
||||
{% } else { %}
|
||||
{%= filters.customer || filters.supplier %}
|
||||
{% } %}
|
||||
</h4>
|
||||
<h6 class="text-center">
|
||||
{% if (filters.tax_id) { %}
|
||||
{%= __("Tax Id: ")%} {%= filters.tax_id %}
|
||||
|
||||
@@ -17,8 +17,9 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"options": "Customer",
|
||||
on_change: () => {
|
||||
var customer = frappe.query_report_filters_by_name.customer.get_value();
|
||||
frappe.db.get_value('Customer', customer, "tax_id", function(value) {
|
||||
frappe.db.get_value('Customer', customer, ["tax_id", "customer_name"], function(value) {
|
||||
frappe.query_report_filters_by_name.tax_id.set_value(value["tax_id"]);
|
||||
frappe.query_report_filters_by_name.customer_name.set_value(value["customer_name"]);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -81,6 +82,12 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"label": __("Tax Id"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"customer_name",
|
||||
"label": __("Customer Name"),
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
@@ -286,7 +286,10 @@ class ReceivablePayableReport(object):
|
||||
|
||||
if party_type == "Supplier":
|
||||
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
|
||||
from `tabPurchase Invoice` where docstatus=1""", as_dict=1):
|
||||
from `tabPurchase Invoice` where docstatus = 1
|
||||
union
|
||||
select name, due_date, bill_no, bill_date from `tabJournal Entry`
|
||||
where docstatus = 1 and bill_no is not NULL""", as_dict=1):
|
||||
voucher_details.setdefault(pi.name, pi)
|
||||
|
||||
return voucher_details
|
||||
|
||||
@@ -71,8 +71,8 @@ def set_account_currency(filters):
|
||||
if gle_currency:
|
||||
account_currency = gle_currency
|
||||
else:
|
||||
account_currency = None if filters.party_type in ["Employee", "Student", "Shareholder"] else \
|
||||
frappe.db.get_value(filters.party_type, filters.party, "default_currency")
|
||||
account_currency = (None if filters.party_type in ["Employee", "Student", "Shareholder", "Member"] else
|
||||
frappe.db.get_value(filters.party_type, filters.party, "default_currency"))
|
||||
|
||||
filters["account_currency"] = account_currency or filters.company_currency
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
|
||||
row += [
|
||||
d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
|
||||
purchase_receipt, expense_account, d.stock_qty, d.stock_uom, d.base_net_rate, d.base_net_amount
|
||||
purchase_receipt, expense_account, d.stock_qty, d.stock_uom, d.base_net_amount / d.stock_qty, d.base_net_amount
|
||||
]
|
||||
|
||||
total_tax = 0
|
||||
@@ -120,8 +120,7 @@ def get_items(filters, additional_query_columns):
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
`tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`,
|
||||
`tabPurchase Invoice Item`.`expense_account`, `tabPurchase Invoice Item`.`stock_qty`,
|
||||
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_rate`,
|
||||
`tabPurchase Invoice Item`.`base_net_amount`,
|
||||
`tabPurchase Invoice Item`.`stock_uom`, `tabPurchase Invoice Item`.`base_net_amount`,
|
||||
`tabPurchase Invoice`.supplier_name, `tabPurchase Invoice`.mode_of_payment {0}
|
||||
from `tabPurchase Invoice`, `tabPurchase Invoice Item`
|
||||
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:maintenance_task",
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2017-10-20 07:10:55.903571",
|
||||
"custom": 0,
|
||||
@@ -42,7 +42,7 @@
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 1
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
@@ -625,7 +625,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-28 12:22:34.151430",
|
||||
"modified": "2018-06-18 16:12:04.330021",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Maintenance Task",
|
||||
|
||||
@@ -7,4 +7,5 @@ import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class AssetMaintenanceTask(Document):
|
||||
pass
|
||||
def autoname(self):
|
||||
self.name = self.maintenance_task
|
||||
|
||||
@@ -2177,6 +2177,65 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_72",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -2490,7 +2549,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-11-29 14:07:56.698355",
|
||||
"modified": "2018-05-28 02:45:48.616334",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Supplier Quotation",
|
||||
|
||||
@@ -82,7 +82,8 @@ class AccountsController(TransactionBase):
|
||||
self.validate_non_invoice_documents_schedule()
|
||||
|
||||
def before_print(self):
|
||||
if self.doctype in ['Purchase Order', 'Sales Order']:
|
||||
if self.doctype in ['Purchase Order', 'Sales Order', 'Sales Invoice', 'Purchase Invoice',
|
||||
'Supplier Quotation', 'Purchase Receipt', 'Delivery Note', 'Quotation']:
|
||||
if self.get("group_same_items"):
|
||||
self.group_similar_items()
|
||||
|
||||
@@ -232,7 +233,7 @@ class AccountsController(TransactionBase):
|
||||
|
||||
tax_master_doctype = self.meta.get_field("taxes_and_charges").options
|
||||
|
||||
if self.is_new() and not self.get("taxes"):
|
||||
if (self.is_new() or self.is_pos_profile_changed()) and not self.get("taxes"):
|
||||
if self.company and not self.get("taxes_and_charges"):
|
||||
# get the default tax master
|
||||
self.taxes_and_charges = frappe.db.get_value(tax_master_doctype,
|
||||
@@ -240,6 +241,11 @@ class AccountsController(TransactionBase):
|
||||
|
||||
self.append_taxes_from_master(tax_master_doctype)
|
||||
|
||||
def is_pos_profile_changed(self):
|
||||
if (self.doctype == 'Sales Invoice' and self.is_pos and
|
||||
self.pos_profile != frappe.db.get_value('Sales Invoice', self.name, 'pos_profile')):
|
||||
return True
|
||||
|
||||
def append_taxes_from_master(self, tax_master_doctype=None):
|
||||
if self.get("taxes_and_charges"):
|
||||
if not tax_master_doctype:
|
||||
@@ -658,6 +664,7 @@ class AccountsController(TransactionBase):
|
||||
if item.item_code in group_item_qty:
|
||||
item.qty = group_item_qty[item.item_code]
|
||||
item.amount = group_item_amount[item.item_code]
|
||||
item.rate = flt(flt(item.amount)/flt(item.qty), item.precision("rate"))
|
||||
del group_item_qty[item.item_code]
|
||||
else:
|
||||
duplicate_list.append(item)
|
||||
|
||||
@@ -35,6 +35,7 @@ class BuyingController(StockController):
|
||||
if getattr(self, "supplier", None) and not self.supplier_name:
|
||||
self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
|
||||
|
||||
self.validate_items()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.validate_warehouse()
|
||||
@@ -456,3 +457,32 @@ class BuyingController(StockController):
|
||||
else:
|
||||
frappe.throw(_("Please enter Reqd by Date"))
|
||||
|
||||
def validate_items(self):
|
||||
# validate items to see if they have is_purchase_item or is_subcontracted_item enabled
|
||||
if self.doctype=="Material Request": return
|
||||
|
||||
if hasattr(self, "is_subcontracted") and self.is_subcontracted == 'Yes':
|
||||
validate_item_type(self, "is_sub_contracted_item", "subcontracted")
|
||||
else:
|
||||
validate_item_type(self, "is_purchase_item", "purchase")
|
||||
|
||||
def validate_item_type(doc, fieldname, message):
|
||||
# iterate through items and check if they are valid sales or purchase items
|
||||
items = [d.item_code for d in doc.items if d.item_code]
|
||||
|
||||
# No validation check inase of creating transaction using 'Opening Invoice Creation Tool'
|
||||
if not items:
|
||||
return
|
||||
|
||||
item_list = ", ".join(["'%s'" % frappe.db.escape(d) for d in items])
|
||||
|
||||
invalid_items = [d[0] for d in frappe.db.sql("""
|
||||
select item_code from tabItem where name in ({0}) and {1}=0
|
||||
""".format(item_list, fieldname), as_list=True)]
|
||||
|
||||
if invalid_items:
|
||||
frappe.throw(_("Following item {items} {verb} not marked as {message} item.\
|
||||
You can enable them as {message} item from its Item master".format(
|
||||
items = ", ".join([d for d in invalid_items]),
|
||||
verb = "are" if len(invalid_items) > 1 else "is",
|
||||
message = message)))
|
||||
|
||||
@@ -157,9 +157,10 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
# scan description only if items are less than 50000
|
||||
description_cond = 'or tabItem.description LIKE %(txt)s'
|
||||
|
||||
return frappe.db.sql("""select tabItem.name, tabItem.item_group,
|
||||
return frappe.db.sql("""select tabItem.name,
|
||||
if(length(tabItem.item_name) > 40,
|
||||
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
|
||||
tabItem.item_group,
|
||||
if(length(tabItem.description) > 40, \
|
||||
concat(substr(tabItem.description, 1, 40), "..."), description) as decription
|
||||
from tabItem
|
||||
|
||||
@@ -83,15 +83,15 @@ def validate_returned_items(doc):
|
||||
else:
|
||||
ref = valid_items.get(d.item_code, frappe._dict())
|
||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
||||
|
||||
|
||||
if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
|
||||
frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}")
|
||||
.format(d.idx, doc.doctype, doc.return_against))
|
||||
|
||||
|
||||
elif ref.batch_no and d.batch_no not in ref.batch_no:
|
||||
frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
|
||||
.format(d.idx, doc.doctype, doc.return_against))
|
||||
|
||||
|
||||
elif ref.serial_no:
|
||||
if not d.serial_no:
|
||||
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
|
||||
@@ -120,25 +120,30 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items):
|
||||
|
||||
for column in fields:
|
||||
returned_qty = flt(already_returned_data.get(column, 0)) if len(already_returned_data) > 0 else 0
|
||||
reference_qty = (ref.get(column) if column == 'stock_qty'
|
||||
else ref.get(column) * ref.get("conversion_factor", 1.0))
|
||||
|
||||
if column == 'stock_qty':
|
||||
reference_qty = ref.get(column)
|
||||
current_stock_qty = args.get(column)
|
||||
else:
|
||||
reference_qty = ref.get(column) * ref.get("conversion_factor", 1.0)
|
||||
current_stock_qty = args.get(column) * args.get("conversion_factor", 1.0)
|
||||
|
||||
max_returnable_qty = flt(reference_qty) - returned_qty
|
||||
label = column.replace('_', ' ').title()
|
||||
|
||||
if reference_qty:
|
||||
if reference_qty:
|
||||
if flt(args.get(column)) > 0:
|
||||
frappe.throw(_("{0} must be negative in return document").format(label))
|
||||
elif returned_qty >= reference_qty and args.get(column):
|
||||
frappe.throw(_("Item {0} has already been returned")
|
||||
.format(args.item_code), StockOverReturnError)
|
||||
elif (abs(args.get(column)) * args.get("conversion_factor", 1.0)) > max_returnable_qty:
|
||||
elif abs(current_stock_qty) > max_returnable_qty:
|
||||
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
|
||||
.format(args.idx, reference_qty, args.item_code), StockOverReturnError)
|
||||
|
||||
def get_ref_item_dict(valid_items, ref_item_row):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
|
||||
valid_items.setdefault(ref_item_row.item_code, frappe._dict({
|
||||
"qty": 0,
|
||||
"rate": 0,
|
||||
@@ -160,10 +165,10 @@ def get_ref_item_dict(valid_items, ref_item_row):
|
||||
|
||||
if ref_item_row.get("serial_no"):
|
||||
item_dict["serial_no"] += get_serial_nos(ref_item_row.serial_no)
|
||||
|
||||
|
||||
if ref_item_row.get("batch_no"):
|
||||
item_dict["batch_no"].append(ref_item_row.batch_no)
|
||||
|
||||
|
||||
return valid_items
|
||||
|
||||
def get_already_returned_items(doc):
|
||||
|
||||
@@ -35,6 +35,7 @@ class SellingController(StockController):
|
||||
|
||||
def validate(self):
|
||||
super(SellingController, self).validate()
|
||||
self.validate_items()
|
||||
self.validate_max_discount()
|
||||
self.validate_selling_price()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
@@ -337,6 +338,11 @@ class SellingController(StockController):
|
||||
po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
|
||||
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
|
||||
|
||||
def validate_items(self):
|
||||
# validate items to see if they have is_sales_item enabled
|
||||
from erpnext.controllers.buying_controller import validate_item_type
|
||||
validate_item_type(self, "is_sales_item", "sales")
|
||||
|
||||
def check_active_sales_items(obj):
|
||||
for d in obj.get("items"):
|
||||
if d.item_code:
|
||||
|
||||
@@ -29,6 +29,7 @@ class calculate_taxes_and_totals(object):
|
||||
self.set_item_wise_tax_breakup()
|
||||
|
||||
def _calculate(self):
|
||||
self.validate_conversion_rate()
|
||||
self.calculate_item_values()
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
@@ -37,6 +38,7 @@ class calculate_taxes_and_totals(object):
|
||||
self.manipulate_grand_total_for_inclusive_tax()
|
||||
self.calculate_totals()
|
||||
self._cleanup()
|
||||
self.calculate_total_net_weight()
|
||||
|
||||
def validate_conversion_rate(self):
|
||||
# validate conversion rate
|
||||
@@ -327,6 +329,13 @@ class calculate_taxes_and_totals(object):
|
||||
|
||||
self.set_rounded_total()
|
||||
|
||||
def calculate_total_net_weight(self):
|
||||
if self.doc.meta.get_field('total_net_weight'):
|
||||
self.doc.total_net_weight = 0.0
|
||||
for d in self.doc.items:
|
||||
if d.total_weight:
|
||||
self.doc.total_net_weight += d.total_weight
|
||||
|
||||
def set_rounded_total(self):
|
||||
if self.doc.meta.get_field("rounded_total"):
|
||||
if self.doc.is_rounded_total_disabled():
|
||||
@@ -557,7 +566,8 @@ def get_itemised_tax_breakup_html(doc):
|
||||
itemised_tax=itemised_tax,
|
||||
itemised_taxable_amount=itemised_taxable_amount,
|
||||
tax_accounts=tax_accounts,
|
||||
company_currency=erpnext.get_company_currency(doc.company)
|
||||
conversion_rate=doc.conversion_rate,
|
||||
currency=doc.currency
|
||||
)
|
||||
)
|
||||
|
||||
@@ -590,16 +600,19 @@ def get_itemised_tax(taxes):
|
||||
for item_code, tax_data in item_tax_map.items():
|
||||
itemised_tax.setdefault(item_code, frappe._dict())
|
||||
|
||||
tax_rate = 0.0
|
||||
tax_amount = 0.0
|
||||
|
||||
if isinstance(tax_data, list):
|
||||
itemised_tax[item_code][tax.description] = frappe._dict(dict(
|
||||
tax_rate=flt(tax_data[0]),
|
||||
tax_amount=flt(tax_data[1])
|
||||
))
|
||||
tax_rate = flt(tax_data[0])
|
||||
tax_amount = flt(tax_data[1])
|
||||
else:
|
||||
itemised_tax[item_code][tax.description] = frappe._dict(dict(
|
||||
tax_rate=flt(tax_data),
|
||||
tax_amount=0.0
|
||||
))
|
||||
tax_rate = flt(tax_data)
|
||||
|
||||
itemised_tax[item_code][tax.description] = frappe._dict(dict(
|
||||
tax_rate = tax_rate,
|
||||
tax_amount = tax_amount
|
||||
))
|
||||
|
||||
return itemised_tax
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ We can also send the feedback request to Customer/User without configuring the
|
||||
Feedback Trigger.
|
||||
|
||||
To request a feedback manually go to respective document e.g. Sales Order, Issue etc.
|
||||
and click on Ask a Feedback option in Menu.
|
||||
and click on Request Feedback option in Menu.
|
||||
|
||||
<img class="screenshot" alt="Setting Condition" src="/docs/assets/img/setup/feedback/manual-feedback-request-option.png">
|
||||
|
||||
|
||||
@@ -82,36 +82,39 @@ frappe.ui.form.on("Student Group", {
|
||||
max_roll_no = d.group_roll_number;
|
||||
}
|
||||
});
|
||||
frappe.call({
|
||||
method: "erpnext.education.doctype.student_group.student_group.get_students",
|
||||
args: {
|
||||
"academic_year": frm.doc.academic_year,
|
||||
"academic_term": frm.doc.academic_term,
|
||||
"group_based_on": frm.doc.group_based_on,
|
||||
"program": frm.doc.program,
|
||||
"batch" : frm.doc.batch,
|
||||
"course": frm.doc.course
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
$.each(r.message, function(i, d) {
|
||||
if(!in_list(student_list, d.student)) {
|
||||
var s = frm.add_child("students");
|
||||
s.student = d.student;
|
||||
s.student_name = d.student_name;
|
||||
if (d.active === 0) {
|
||||
s.active = 0;
|
||||
|
||||
if(frm.doc.academic_year) {
|
||||
frappe.call({
|
||||
method: "erpnext.education.doctype.student_group.student_group.get_students",
|
||||
args: {
|
||||
"academic_year": frm.doc.academic_year,
|
||||
"academic_term": frm.doc.academic_term,
|
||||
"group_based_on": frm.doc.group_based_on,
|
||||
"program": frm.doc.program,
|
||||
"batch" : frm.doc.batch,
|
||||
"course": frm.doc.course
|
||||
},
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
$.each(r.message, function(i, d) {
|
||||
if(!in_list(student_list, d.student)) {
|
||||
var s = frm.add_child("students");
|
||||
s.student = d.student;
|
||||
s.student_name = d.student_name;
|
||||
if (d.active === 0) {
|
||||
s.active = 0;
|
||||
}
|
||||
s.group_roll_number = ++max_roll_no;
|
||||
}
|
||||
s.group_roll_number = ++max_roll_no;
|
||||
}
|
||||
});
|
||||
refresh_field("students");
|
||||
frm.save();
|
||||
} else {
|
||||
frappe.msgprint(__("Student Group is already updated."))
|
||||
});
|
||||
refresh_field("students");
|
||||
frm.save();
|
||||
} else {
|
||||
frappe.msgprint(__("Student Group is already updated."))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
frappe.msgprint(__("Select students manually for the Activity based Group"));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ frappe.ui.form.on('Student Report Generation Tool', {
|
||||
frm.page.clear_indicator();
|
||||
frm.page.set_primary_action(__('Print Report Card'), () => {
|
||||
let url = "/api/method/erpnext.education.doctype.student_report_generation_tool.student_report_generation_tool.preview_report_card";
|
||||
open_url_post(url, frm.doc, true);
|
||||
open_url_post(url, {"doc": frm.doc}, true);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe, json
|
||||
from frappe.model.document import Document
|
||||
from erpnext.education.api import get_grade
|
||||
from frappe.utils.pdf import get_pdf
|
||||
@@ -16,8 +16,8 @@ class StudentReportGenerationTool(Document):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def preview_report_card(**kwargs):
|
||||
doc = frappe._dict(**kwargs)
|
||||
def preview_report_card(doc):
|
||||
doc = frappe._dict(json.loads(doc))
|
||||
doc.students = [doc.student]
|
||||
if not (doc.student_name and doc.student_batch):
|
||||
program_enrollment = frappe.get_all("Program Enrollment", fields=["student_batch_name", "student_name"],
|
||||
@@ -33,7 +33,7 @@ def preview_report_card(**kwargs):
|
||||
course_criteria = get_courses_criteria(courses)
|
||||
|
||||
# get the assessment group as per the user selection
|
||||
if int(doc.include_all_assessment):
|
||||
if doc.include_all_assessment:
|
||||
assessment_groups = get_child_assessment_groups(doc.assessment_group)
|
||||
else:
|
||||
assessment_groups = [doc.assessment_group]
|
||||
@@ -55,7 +55,7 @@ def preview_report_card(**kwargs):
|
||||
"assessment_groups": assessment_groups,
|
||||
"course_criteria": course_criteria,
|
||||
"letterhead": letterhead.content,
|
||||
"add_letterhead": int(doc.add_letterhead) if int(doc.add_letterhead) else 0
|
||||
"add_letterhead": doc.add_letterhead if doc.add_letterhead else 0
|
||||
})
|
||||
final_template = frappe.render_template(base_template_path, {"body": html, "title": "Report Card"})
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils import getdate, cstr
|
||||
import json
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
|
||||
|
||||
@@ -113,9 +113,11 @@ def insert_consultation_to_medical_record(doc):
|
||||
|
||||
def update_consultation_to_medical_record(consultation):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s", (consultation.name))
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
subject = set_subject_field(consultation)
|
||||
frappe.db.set_value("Patient Medical Record", medical_record_id[0][0], "subject", subject)
|
||||
else:
|
||||
insert_consultation_to_medical_record(consultation)
|
||||
|
||||
def delete_medical_record(consultation):
|
||||
frappe.db.sql("""delete from `tabPatient Medical Record` where reference_name = %s""", (consultation.name))
|
||||
@@ -123,7 +125,7 @@ def delete_medical_record(consultation):
|
||||
def set_subject_field(consultation):
|
||||
subject = "No Diagnosis "
|
||||
if(consultation.diagnosis):
|
||||
subject = "Diagnosis: \n"+ str(consultation.diagnosis)+". "
|
||||
subject = "Diagnosis: \n"+ cstr(consultation.diagnosis)+". "
|
||||
if(consultation.drug_prescription):
|
||||
subject +="\nDrug(s) Prescribed. "
|
||||
if(consultation.test_prescription):
|
||||
|
||||
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
import json
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils import getdate, cstr
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account
|
||||
from frappe import _
|
||||
|
||||
@@ -228,9 +228,9 @@ def get_employee_by_user_id(user_id):
|
||||
return employee
|
||||
|
||||
def insert_lab_test_to_medical_record(doc):
|
||||
subject = str(doc.test_name)
|
||||
subject = cstr(doc.test_name)
|
||||
if(doc.test_comment):
|
||||
subject += ", \n"+str(doc.test_comment)
|
||||
subject += ", \n"+ cstr(doc.test_comment)
|
||||
medical_record = frappe.new_doc("Patient Medical Record")
|
||||
medical_record.patient = doc.patient
|
||||
medical_record.subject = subject
|
||||
@@ -244,7 +244,7 @@ def insert_lab_test_to_medical_record(doc):
|
||||
def delete_lab_test_from_medical_record(self):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(self.name))
|
||||
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
frappe.delete_doc("Patient Medical Record", medical_record_id[0][0])
|
||||
|
||||
def create_item_line(test_code, sales_invoice):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
frappe.views.calendar["Patient Appointment"] = {
|
||||
field_map: {
|
||||
"start": "start",
|
||||
"end": "end",
|
||||
"start": "appointment_date",
|
||||
"end": "appointment_datetime",
|
||||
"id": "name",
|
||||
"title": "patient",
|
||||
"allDay": "allDay"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cstr
|
||||
|
||||
class VitalSigns(Document):
|
||||
def on_submit(self):
|
||||
@@ -27,22 +28,22 @@ def insert_vital_signs_to_medical_record(doc):
|
||||
|
||||
def delete_vital_signs_from_medical_record(doc):
|
||||
medical_record_id = frappe.db.sql("select name from `tabPatient Medical Record` where reference_name=%s",(doc.name))
|
||||
if(medical_record_id[0][0]):
|
||||
if medical_record_id and medical_record_id[0][0]:
|
||||
frappe.delete_doc("Patient Medical Record", medical_record_id[0][0])
|
||||
|
||||
def set_subject_field(doc):
|
||||
subject = " "
|
||||
if(doc.temperature):
|
||||
subject += "Temperature: \n"+ str(doc.temperature)+". "
|
||||
subject += "Temperature: \n"+ cstr(doc.temperature)+". "
|
||||
if(doc.pulse):
|
||||
subject += "Pulse: \n"+ str(doc.pulse)+". "
|
||||
subject += "Pulse: \n"+ cstr(doc.pulse)+". "
|
||||
if(doc.respiratory_rate):
|
||||
subject += "Respiratory Rate: \n"+ str(doc.respiratory_rate)+". "
|
||||
subject += "Respiratory Rate: \n"+ cstr(doc.respiratory_rate)+". "
|
||||
if(doc.bp):
|
||||
subject += "BP: \n"+ str(doc.bp)+". "
|
||||
subject += "BP: \n"+ cstr(doc.bp)+". "
|
||||
if(doc.bmi):
|
||||
subject += "BMI: \n"+ str(doc.bmi)+". "
|
||||
subject += "BMI: \n"+ cstr(doc.bmi)+". "
|
||||
if(doc.nutrition_note):
|
||||
subject += "Note: \n"+ str(doc.nutrition_note)+". "
|
||||
subject += "Note: \n"+ cstr(doc.nutrition_note)+". "
|
||||
|
||||
return subject
|
||||
|
||||
@@ -20,17 +20,19 @@ class Attendance(Document):
|
||||
set_employee_name(self)
|
||||
|
||||
def check_leave_record(self):
|
||||
leave_record = frappe.db.sql("""select leave_type, half_day from `tabLeave Application`
|
||||
leave_record = frappe.db.sql("""select leave_type, half_day, half_day_date from `tabLeave Application`
|
||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
||||
and docstatus = 1""", (self.employee, self.attendance_date), as_dict=True)
|
||||
if leave_record:
|
||||
if leave_record[0].half_day:
|
||||
self.status = 'Half Day'
|
||||
frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date))
|
||||
else:
|
||||
self.status = 'On Leave'
|
||||
self.leave_type = leave_record[0].leave_type
|
||||
frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date))
|
||||
for d in leave_record:
|
||||
if d.half_day_date == getdate(self.attendance_date):
|
||||
self.status = 'Half Day'
|
||||
frappe.msgprint(_("Employee {0} on Half day on {1}").format(self.employee, self.attendance_date))
|
||||
else:
|
||||
self.status = 'On Leave'
|
||||
self.leave_type = d.leave_type
|
||||
frappe.msgprint(_("Employee {0} on Leave on {1}").format(self.employee, self.attendance_date))
|
||||
|
||||
if self.status == "On Leave" and not leave_record:
|
||||
frappe.throw(_("No leave record found for employee {0} for {1}").format(self.employee, self.attendance_date))
|
||||
|
||||
|
||||
@@ -45,8 +45,9 @@ class ExpenseClaim(AccountsController):
|
||||
}[cstr(self.docstatus or 0)]
|
||||
|
||||
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
|
||||
precision = self.precision("total_sanctioned_amount")
|
||||
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
|
||||
and flt(self.total_sanctioned_amount) == paid_amount)) \
|
||||
and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \
|
||||
and self.docstatus == 1 and self.approval_status == 'Approved':
|
||||
self.status = "Paid"
|
||||
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
frappe.views.calendar["Holiday List"] = {
|
||||
field_map: {
|
||||
"start": "holiday_date",
|
||||
"end": "holiday_date",
|
||||
"start": "from_date",
|
||||
"end": "to_date",
|
||||
"id": "name",
|
||||
"title": "description",
|
||||
"allDay": "allDay"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
@@ -11,6 +12,7 @@
|
||||
"editable_grid": 1,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -21,6 +23,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee Settings",
|
||||
@@ -38,6 +41,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -50,6 +54,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Retirement Age",
|
||||
@@ -68,6 +73,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -80,6 +86,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Employee Records to be created by",
|
||||
@@ -98,6 +105,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -108,6 +116,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
@@ -125,6 +134,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -136,6 +146,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Stop Birthday Reminders",
|
||||
@@ -153,6 +164,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -163,6 +175,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Maintain Billing Hours and Working Hours Same on Timesheet",
|
||||
@@ -181,6 +194,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -191,6 +205,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Payroll Settings",
|
||||
@@ -208,6 +223,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -219,6 +235,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Include holidays in Total no. of Working Days",
|
||||
@@ -236,6 +253,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -248,6 +266,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Email Salary Slip to Employee",
|
||||
@@ -266,6 +285,7 @@
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -276,6 +296,7 @@
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Max working hours against Timesheet",
|
||||
@@ -292,21 +313,81 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "leave_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Leave Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "show_leaves_of_all_department_members_in_calendar",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Show Leaves Of All Department Members In Calendar",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"in_dialog": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-01-16 14:01:31.183485",
|
||||
"modified_by": "anastasiadis.st00@gmail.com",
|
||||
"modified": "2018-05-03 15:35:13.015466",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "HR Settings",
|
||||
"owner": "Administrator",
|
||||
@@ -321,7 +402,6 @@
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"is_custom": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
@@ -336,6 +416,7 @@
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
|
||||
23
erpnext/hr/doctype/hr_settings/test_hr_settings.js
Normal file
23
erpnext/hr/doctype/hr_settings/test_hr_settings.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/* eslint-disable */
|
||||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: HR Settings", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new HR Settings
|
||||
() => frappe.tests.make('HR Settings', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
() => {
|
||||
assert.equal(cur_frm.doc.key, 'value');
|
||||
},
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
10
erpnext/hr/doctype/hr_settings/test_hr_settings.py
Normal file
10
erpnext/hr/doctype/hr_settings/test_hr_settings.py
Normal file
@@ -0,0 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestHRSettings(unittest.TestCase):
|
||||
pass
|
||||
@@ -42,6 +42,7 @@ frappe.ui.form.on("Leave Application", {
|
||||
|
||||
employee: function(frm) {
|
||||
frm.trigger("get_leave_balance");
|
||||
frm.trigger("set_leave_approver");
|
||||
},
|
||||
|
||||
leave_type: function(frm) {
|
||||
@@ -95,6 +96,9 @@ frappe.ui.form.on("Leave Application", {
|
||||
if (!r.exc && r.message) {
|
||||
frm.set_value('leave_balance', r.message);
|
||||
}
|
||||
else {
|
||||
frm.set_value('leave_balance', "0");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -122,4 +126,21 @@ frappe.ui.form.on("Leave Application", {
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
set_leave_approver: function(frm) {
|
||||
if(frm.doc.employee) {
|
||||
// server call is done to include holidays in leave days calculations
|
||||
return frappe.call({
|
||||
method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver_data',
|
||||
args: {
|
||||
"employee": frm.doc.employee,
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r && r.message) {
|
||||
frm.set_value('leave_approver', r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,12 +5,12 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, \
|
||||
comma_or, get_fullname
|
||||
comma_or, get_fullname, nowdate
|
||||
from erpnext.hr.utils import set_employee_name
|
||||
from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates
|
||||
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||
from erpnext.hr.doctype.employee_leave_approver.employee_leave_approver import get_approver_list
|
||||
|
||||
from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange
|
||||
|
||||
class LeaveDayBlockedError(frappe.ValidationError): pass
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
@@ -52,6 +52,7 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Only Leave Applications with status 'Approved' and 'Rejected' can be submitted"))
|
||||
|
||||
self.validate_back_dated_application()
|
||||
self.update_attendance()
|
||||
|
||||
# notify leave applier about approval
|
||||
self.notify_employee(self.status)
|
||||
@@ -59,6 +60,7 @@ class LeaveApplication(Document):
|
||||
def on_cancel(self):
|
||||
# notify leave applier about cancellation
|
||||
self.notify_employee("cancelled")
|
||||
self.cancel_attendance()
|
||||
|
||||
def validate_dates(self):
|
||||
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
|
||||
@@ -100,6 +102,50 @@ class LeaveApplication(Document):
|
||||
frappe.throw(_("Leave cannot be applied/cancelled before {0}, as leave balance has already been carry-forwarded in the future leave allocation record {1}")
|
||||
.format(formatdate(future_allocation[0].from_date), future_allocation[0].name))
|
||||
|
||||
def update_attendance(self):
|
||||
if self.status == "Approved":
|
||||
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\
|
||||
and (attendance_date between %s and %s) and docstatus < 2""",(self.employee, self.from_date, self.to_date), as_dict=1)
|
||||
|
||||
if attendance:
|
||||
for d in attendance:
|
||||
doc = frappe.get_doc("Attendance", d.name)
|
||||
if getdate(self.half_day_date) == doc.attendance_date:
|
||||
status = "Half Day"
|
||||
else:
|
||||
status = "On Leave"
|
||||
frappe.db.sql("""update `tabAttendance` set status = %s, leave_type = %s\
|
||||
where name = %s""",(status, self.leave_type, d.name))
|
||||
|
||||
elif getdate(self.to_date) <= getdate(nowdate()):
|
||||
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
|
||||
date = dt.strftime("%Y-%m-%d")
|
||||
if not date == self.half_day_date:
|
||||
doc = frappe.new_doc("Attendance")
|
||||
doc.employee = self.employee
|
||||
doc.attendance_date = date
|
||||
doc.company = self.company
|
||||
doc.status = "On Leave"
|
||||
doc.leave_type = self.leave_type
|
||||
doc.insert(ignore_permissions=True)
|
||||
doc.submit()
|
||||
else:
|
||||
doc = frappe.new_doc("Attendance")
|
||||
doc.employee = self.employee
|
||||
doc.attendance_date = date
|
||||
doc.company = self.company
|
||||
doc.status = "Half Day"
|
||||
doc.leave_type = self.leave_type
|
||||
doc.insert(ignore_permissions=True)
|
||||
doc.submit()
|
||||
|
||||
def cancel_attendance(self):
|
||||
if self.docstatus == 2:
|
||||
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\
|
||||
and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""",(self.employee, self.from_date, self.to_date), as_dict=1)
|
||||
for name in attendance:
|
||||
frappe.db.set_value("Attendance", name, "docstatus", 2)
|
||||
|
||||
def validate_salary_processed_days(self):
|
||||
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
|
||||
return
|
||||
@@ -494,3 +540,8 @@ def add_holidays(events, start, end, employee, company):
|
||||
"title": _("Holiday") + ": " + cstr(holiday.description),
|
||||
"name": holiday.name
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_leave_approver_data(employee):
|
||||
return frappe.db.get_value("Employee Leave Approver",
|
||||
{'parent': employee}, 'leave_approver')
|
||||
|
||||
@@ -146,6 +146,7 @@ class PayrollEntry(Document):
|
||||
ss_list = self.get_sal_slip_list(ss_status=0)
|
||||
submitted_ss = []
|
||||
not_submitted_ss = []
|
||||
frappe.flags.via_payroll_entry = True
|
||||
for ss in ss_list:
|
||||
ss_obj = frappe.get_doc("Salary Slip",ss[0])
|
||||
ss_dict = {}
|
||||
@@ -159,7 +160,7 @@ class PayrollEntry(Document):
|
||||
else:
|
||||
try:
|
||||
ss_obj.submit()
|
||||
submitted_ss.append(ss_dict)
|
||||
submitted_ss.append(ss_obj)
|
||||
|
||||
except frappe.ValidationError:
|
||||
not_submitted_ss.append(ss_dict)
|
||||
@@ -168,8 +169,15 @@ class PayrollEntry(Document):
|
||||
frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}")
|
||||
.format(ss_obj.start_date, ss_obj.end_date))
|
||||
|
||||
self.email_salary_slip(submitted_ss)
|
||||
|
||||
return create_submit_log(submitted_ss, not_submitted_ss, jv_name)
|
||||
|
||||
def email_salary_slip(self, submitted_ss):
|
||||
if frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee"):
|
||||
for ss in submitted_ss:
|
||||
ss.email_salary_slip()
|
||||
|
||||
def get_loan_details(self):
|
||||
"""
|
||||
Get loan details from submitted salary slip based on selected criteria
|
||||
|
||||
@@ -29,9 +29,10 @@ frappe.ui.form.on("Salary Slip", {
|
||||
})
|
||||
},
|
||||
|
||||
start_date: function(frm){
|
||||
start_date: function(frm, dt, dn){
|
||||
if(frm.doc.start_date){
|
||||
frm.trigger("set_end_date");
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -65,18 +66,20 @@ frappe.ui.form.on("Salary Slip", {
|
||||
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
|
||||
},
|
||||
|
||||
salary_slip_based_on_timesheet: function(frm) {
|
||||
salary_slip_based_on_timesheet: function(frm, dt, dn) {
|
||||
frm.trigger("toggle_fields");
|
||||
frm.set_value('start_date', '');
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
payroll_frequency: function(frm) {
|
||||
payroll_frequency: function(frm, dt, dn) {
|
||||
frm.trigger("toggle_fields");
|
||||
frm.set_value('end_date', '');
|
||||
frm.set_value('start_date', '');
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
employee: function(frm){
|
||||
frm.set_value('start_date', '');
|
||||
employee: function(frm, dt, dn) {
|
||||
get_emp_and_leave_details(frm.doc, dt, dn);
|
||||
},
|
||||
|
||||
toggle_fields: function(frm) {
|
||||
@@ -109,7 +112,7 @@ frappe.ui.form.on('Salary Slip Timesheet', {
|
||||
|
||||
// Get leave details
|
||||
//---------------------------------------------------------------------
|
||||
cur_frm.cscript.start_date = function(doc, dt, dn){
|
||||
var get_emp_and_leave_details = function(doc, dt, dn) {
|
||||
if(!doc.start_date){
|
||||
return frappe.call({
|
||||
method: 'get_emp_and_leave_details',
|
||||
@@ -122,11 +125,9 @@ cur_frm.cscript.start_date = function(doc, dt, dn){
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date;
|
||||
|
||||
cur_frm.cscript.employee = function(doc,dt,dn){
|
||||
doc.salary_structure = ''
|
||||
cur_frm.cscript.start_date(doc, dt, dn)
|
||||
get_emp_and_leave_details(doc, dt, dn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
|
||||
|
||||
@@ -14,8 +14,12 @@ from erpnext.utilities.transaction_base import TransactionBase
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
|
||||
class SalarySlip(TransactionBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalarySlip, self).__init__(*args, **kwargs)
|
||||
self.series = 'Sal Slip/{0}/.#####'.format(self.employee)
|
||||
|
||||
def autoname(self):
|
||||
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
|
||||
self.name = make_autoname(self.series)
|
||||
|
||||
def validate(self):
|
||||
self.status = self.get_status()
|
||||
@@ -411,13 +415,17 @@ class SalarySlip(TransactionBase):
|
||||
else:
|
||||
self.set_status()
|
||||
self.update_status(self.name)
|
||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
|
||||
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")) and not frappe.flags.via_payroll_entry:
|
||||
self.email_salary_slip()
|
||||
|
||||
def on_cancel(self):
|
||||
self.set_status()
|
||||
self.update_status()
|
||||
|
||||
def on_trash(self):
|
||||
from frappe.model.naming import revert_series_if_last
|
||||
revert_series_if_last(self.series, self.name)
|
||||
|
||||
def email_salary_slip(self):
|
||||
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
|
||||
|
||||
|
||||
@@ -161,6 +161,28 @@ frappe.ui.form.on('Salary Structure', {
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Salary Structure Employee', {
|
||||
from_date: function(frm, cdt, cdn) {
|
||||
validate_date(frm, cdt, cdn);
|
||||
},
|
||||
to_date: function(frm, cdt, cdn) {
|
||||
validate_date(frm, cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
var validate_date = function(frm, cdt, cdn) {
|
||||
var doc = locals[cdt][cdn];
|
||||
if(doc.to_date && doc.from_date) {
|
||||
var from_date = frappe.datetime.str_to_obj(doc.from_date);
|
||||
var to_date = frappe.datetime.str_to_obj(doc.to_date);
|
||||
|
||||
if(to_date < from_date) {
|
||||
frappe.model.set_value(cdt, cdn, "to_date", "");
|
||||
frappe.throw(__("From Date cannot be greater than To Date"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.amount = function(doc, cdt, cdn){
|
||||
calculate_totals(doc, cdt, cdn);
|
||||
|
||||
@@ -22,6 +22,10 @@ def execute(filters=None):
|
||||
holiday_map = get_holiday(holiday_list, filters["month"])
|
||||
|
||||
data = []
|
||||
leave_types = frappe.db.sql("""select name from `tabLeave Type`""", as_list=True)
|
||||
leave_list = [d[0] for d in leave_types]
|
||||
columns.extend(leave_list)
|
||||
|
||||
for emp in sorted(att_map):
|
||||
emp_det = emp_map.get(emp)
|
||||
if not emp_det:
|
||||
@@ -49,10 +53,35 @@ def execute(filters=None):
|
||||
elif status == "Half Day":
|
||||
total_p += 0.5
|
||||
total_a += 0.5
|
||||
total_l += 0.5
|
||||
|
||||
row += [total_p, total_l, total_a]
|
||||
data.append(row)
|
||||
|
||||
if not filters.get("employee"):
|
||||
filters.update({"employee": emp})
|
||||
conditions += " and employee = %(employee)s"
|
||||
elif not filters.get("employee") == emp:
|
||||
filters.update({"employee": emp})
|
||||
|
||||
leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\
|
||||
where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1)
|
||||
|
||||
leaves = {}
|
||||
for d in leave_details:
|
||||
if d.status == "Half Day":
|
||||
d.count = d.count * 0.5
|
||||
if d.leave_type in leaves:
|
||||
leaves[d.leave_type] += d.count
|
||||
else:
|
||||
leaves[d.leave_type] = d.count
|
||||
|
||||
for d in leave_list:
|
||||
if d in leaves:
|
||||
row.append(leaves[d])
|
||||
else:
|
||||
row.append("0.0")
|
||||
|
||||
data.append(row)
|
||||
return columns, data
|
||||
|
||||
def get_columns(filters):
|
||||
|
||||
@@ -158,7 +158,7 @@ class BOM(WebsiteGenerator):
|
||||
if not self.buying_price_list:
|
||||
frappe.throw(_("Please select Price List"))
|
||||
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
|
||||
"item_code": arg["item_code"]}, "price_list_rate")
|
||||
"item_code": arg["item_code"]}, "price_list_rate") or 0.0
|
||||
|
||||
price_list_currency = frappe.db.get_value("Price List",
|
||||
self.buying_price_list, "currency")
|
||||
@@ -652,4 +652,4 @@ def get_boms_in_bottom_up_order(bom_no=None):
|
||||
bom_list.append(child_bom)
|
||||
count += 1
|
||||
|
||||
return bom_list
|
||||
return bom_list
|
||||
|
||||
@@ -64,7 +64,7 @@ class ProductionOrder(Document):
|
||||
so.name, so_item.delivery_date, so.project
|
||||
from
|
||||
`tabSales Order` so, `tabSales Order Item` so_item, `tabPacked Item` packed_item
|
||||
where so.name=%s
|
||||
where so.name=%s
|
||||
and so.name=so_item.parent
|
||||
and so.name=packed_item.parent
|
||||
and so_item.item_code = packed_item.parent_item
|
||||
@@ -88,7 +88,7 @@ class ProductionOrder(Document):
|
||||
self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
|
||||
if not self.fg_warehouse:
|
||||
self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
|
||||
|
||||
|
||||
def validate_warehouse_belongs_to_company(self):
|
||||
warehouses = [self.fg_warehouse, self.wip_warehouse]
|
||||
for d in self.get("required_items"):
|
||||
@@ -524,7 +524,7 @@ def get_item_details(item, project = None):
|
||||
if not res["bom_no"]:
|
||||
if project:
|
||||
res = get_item_details(item)
|
||||
frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project))
|
||||
frappe.msgprint(_("Default BOM not found for Item {0} and Project {1}").format(item, project), alert=1)
|
||||
else:
|
||||
frappe.throw(_("Default BOM for {0} not found").format(item))
|
||||
|
||||
@@ -642,5 +642,5 @@ def query_sales_order(production_item):
|
||||
select distinct so.name from `tabSales Order` so, `tabPacked Item` pi_item
|
||||
where pi_item.parent=so.name and pi_item.item_code=%s and so.docstatus=1
|
||||
""", (production_item, production_item))
|
||||
|
||||
|
||||
return out
|
||||
|
||||
@@ -64,8 +64,7 @@ erpnext.ProductionAnalytics = frappe.views.GridReportWithPlot.extend({
|
||||
|
||||
var chart_data = this.get_chart_data ? this.get_chart_data() : null;
|
||||
|
||||
this.chart = new Chart({
|
||||
parent: ".chart",
|
||||
this.chart = new frappeChart.Chart(".chart", {
|
||||
data: chart_data,
|
||||
type: 'line'
|
||||
});
|
||||
|
||||
@@ -498,4 +498,8 @@ erpnext.patches.v10_0.update_reserved_qty_for_purchase_order
|
||||
erpnext.patches.v10_0.update_hub_connector_domain
|
||||
erpnext.patches.v10_0.set_student_party_type
|
||||
erpnext.patches.v10_0.update_project_in_sle
|
||||
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
|
||||
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
|
||||
erpnext.patches.v10_0.taxes_issue_with_pos
|
||||
erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input
|
||||
erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar
|
||||
erpnext.patches.v10_0.update_status_in_purchase_receipt
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2017, Frappe 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_settings")
|
||||
|
||||
ss = frappe.get_doc("Stock Settings")
|
||||
ss.set_qty_in_transactions_based_on_serial_no_input = 1
|
||||
|
||||
if ss.default_warehouse \
|
||||
and not frappe.db.exists("Warehouse", ss.default_warehouse):
|
||||
ss.default_warehouse = None
|
||||
|
||||
if ss.stock_uom and not frappe.db.exists("UOM", ss.stock_uom):
|
||||
ss.stock_uom = None
|
||||
|
||||
ss.flags.ignore_mandatory = True
|
||||
ss.save()
|
||||
@@ -0,0 +1,5 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("hr", "doctype", "hr_settings")
|
||||
frappe.db.set_value("HR Settings", None, "show_leaves_of_all_department_members_in_calendar", 1)
|
||||
26
erpnext/patches/v10_0/taxes_issue_with_pos.py
Normal file
26
erpnext/patches/v10_0/taxes_issue_with_pos.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2017, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
for d in frappe.get_all('Sales Invoice', fields=["name"],
|
||||
filters = {'is_pos':1, 'docstatus': 1, 'creation': ('>', '2018-04-23')}):
|
||||
doc = frappe.get_doc('Sales Invoice', d.name)
|
||||
if (not doc.taxes and doc.taxes_and_charges and doc.pos_profile and doc.outstanding_amount != 0 and
|
||||
frappe.db.get_value('POS Profile', doc.pos_profile, 'taxes_and_charges', cache=True) == doc.taxes_and_charges):
|
||||
|
||||
doc.append_taxes_from_master()
|
||||
doc.calculate_taxes_and_totals()
|
||||
for d in doc.taxes:
|
||||
d.db_update()
|
||||
|
||||
doc.db_update()
|
||||
|
||||
delete_gle_for_voucher(doc.name)
|
||||
doc.make_gl_entries(repost_future_gle=False)
|
||||
|
||||
def delete_gle_for_voucher(voucher_no):
|
||||
frappe.db.sql("""delete from `tabGL Entry` where voucher_no = %(voucher_no)s""",
|
||||
{'voucher_no': voucher_no})
|
||||
@@ -0,0 +1,7 @@
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("stock", "doctype", "purchase_receipt")
|
||||
frappe.db.sql('''
|
||||
UPDATE `tabPurchase Receipt` SET status = "Completed" WHERE per_billed = 100 AND docstatus = 1
|
||||
''')
|
||||
@@ -75,7 +75,7 @@ class Project(Document):
|
||||
sum = 0
|
||||
for task in self.tasks:
|
||||
if task.task_weight > 0:
|
||||
sum = sum + task.task_weight
|
||||
sum = flt(sum + task.task_weight, task.precision('task_weight'))
|
||||
if sum > 0 and sum != 1:
|
||||
frappe.throw(_("Total of all task weights should be 1. Please adjust weights of all Project tasks accordingly"))
|
||||
|
||||
|
||||
@@ -420,9 +420,10 @@ def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20
|
||||
# find customer name from contact.
|
||||
customer = frappe.db.sql('''SELECT dl.link_name FROM `tabContact` AS c inner join \
|
||||
`tabDynamic Link` AS dl ON c.first_name=dl.link_name WHERE c.email_id=%s''',user)
|
||||
# find list of Sales Invoice for made for customer.
|
||||
sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
|
||||
|
||||
if customer:
|
||||
# find list of Sales Invoice for made for customer.
|
||||
sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
|
||||
# Return timesheet related data to web portal.
|
||||
return frappe. db.sql('''SELECT ts.name, tsd.activity_type, ts.status, ts.total_billable_hours, \
|
||||
tsd.sales_invoice, tsd.project FROM `tabTimesheet` AS ts inner join `tabTimesheet Detail` \
|
||||
|
||||
@@ -338,12 +338,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
|
||||
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
|
||||
// store tax breakup for each item
|
||||
var key = item.item_code || item.item_name;
|
||||
var item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
|
||||
if (tax.item_wise_tax_detail && tax.item_wise_tax_detail[key])
|
||||
item_wise_tax_amount += tax.item_wise_tax_detail[key][1];
|
||||
let tax_detail = tax.item_wise_tax_detail;
|
||||
let key = item.item_code || item.item_name;
|
||||
|
||||
tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
|
||||
let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
|
||||
if (tax_detail && tax_detail[key])
|
||||
item_wise_tax_amount += tax_detail[key][1];
|
||||
|
||||
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
|
||||
},
|
||||
|
||||
round_off_totals: function(tax) {
|
||||
|
||||
@@ -106,7 +106,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
|
||||
if(
|
||||
this.frm.docstatus < 2
|
||||
this.frm.docstatus < 2
|
||||
&& this.frm.fields_dict["payment_terms_template"]
|
||||
&& this.frm.fields_dict["payment_schedule"]
|
||||
&& this.frm.doc.payment_terms_template
|
||||
@@ -401,7 +401,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
item.serial_no = valid_serial_nos.join('\n');
|
||||
|
||||
refresh_field("serial_no", item.name, item.parentfield);
|
||||
if(!doc.is_return) {
|
||||
if(!doc.is_return && cint(user_defaults.set_qty_in_transactions_based_on_serial_no_input)) {
|
||||
frappe.model.set_value(item.doctype, item.name,
|
||||
"qty", valid_serial_nos.length / item.conversion_factor);
|
||||
frappe.model.set_value(item.doctype, item.name, "stock_qty", valid_serial_nos.length);
|
||||
@@ -1094,6 +1094,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
me.in_apply_price_list = false;
|
||||
}
|
||||
}
|
||||
}).always(() => {
|
||||
me.in_apply_price_list = false;
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -169,21 +169,25 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
|
||||
}
|
||||
|
||||
erpnext.utils.get_shipping_address = function(frm, callback){
|
||||
frappe.call({
|
||||
method: "frappe.contacts.doctype.address.address.get_shipping_address",
|
||||
args: {
|
||||
company: frm.doc.company,
|
||||
address: frm.doc.shipping_address
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
frm.set_value("shipping_address", r.message[0]) //Address title or name
|
||||
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
|
||||
}
|
||||
if (frm.doc.company) {
|
||||
frappe.call({
|
||||
method: "frappe.contacts.doctype.address.address.get_shipping_address",
|
||||
args: {
|
||||
company: frm.doc.company,
|
||||
address: frm.doc.shipping_address
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
frm.set_value("shipping_address", r.message[0]) //Address title or name
|
||||
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
|
||||
}
|
||||
|
||||
if(callback){
|
||||
return callback();
|
||||
if(callback){
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
frappe.msgprint(__("Select company first"));
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class Gstr1Report(object):
|
||||
posting_date,
|
||||
base_grand_total,
|
||||
base_rounded_total,
|
||||
customer_gstin,
|
||||
COALESCE(NULLIF(customer_gstin,''), NULLIF(billing_address_gstin, '')) as customer_gstin,
|
||||
place_of_supply,
|
||||
ecommerce_gstin,
|
||||
reverse_charge,
|
||||
@@ -144,7 +144,10 @@ class Gstr1Report(object):
|
||||
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
|
||||
|
||||
for d in items:
|
||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, d.base_net_amount)
|
||||
if d.item_code not in self.invoice_items.get(d.parent, {}):
|
||||
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
|
||||
sum(i.get('base_net_amount', 0) for i in items
|
||||
if i.item_code == d.item_code and i.parent == d.parent))
|
||||
|
||||
def get_items_based_on_tax_rate(self):
|
||||
self.tax_details = frappe.db.sql("""
|
||||
|
||||
@@ -79,7 +79,7 @@ class Customer(TransactionBase):
|
||||
def update_customer_groups(self):
|
||||
ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
|
||||
if frappe.flags.customer_group_changed:
|
||||
update_linked_doctypes('Customer', self.name, 'Customer Group',
|
||||
update_linked_doctypes('Customer', frappe.db.escape(self.name), 'Customer Group',
|
||||
self.customer_group, ignore_doctypes)
|
||||
|
||||
def create_primary_contact(self):
|
||||
@@ -316,7 +316,7 @@ def make_address(args, is_primary_address=1):
|
||||
return address
|
||||
|
||||
def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, filters):
|
||||
customer = frappe.db.escape(filters.get('customer'))
|
||||
customer = filters.get('customer')
|
||||
return frappe.db.sql("""
|
||||
select `tabContact`.name from `tabContact`, `tabDynamic Link`
|
||||
where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s
|
||||
|
||||
@@ -2439,8 +2439,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_print_heading",
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -2448,18 +2448,16 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Heading",
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "select_print_heading",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Print Heading",
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 1,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
@@ -2496,6 +2494,38 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_print_heading",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Heading",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "select_print_heading",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Print Heading",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -2881,8 +2911,8 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 1,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-12-19 14:52:28.966139",
|
||||
"modified_by": "nabinhait@gmail.com",
|
||||
"modified": "2018-05-28 03:23:15.354674",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
"owner": "Administrator",
|
||||
|
||||
@@ -185,6 +185,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
|
||||
target.run_method("calculate_taxes_and_totals")
|
||||
|
||||
def update_item(obj, target, source_parent):
|
||||
target.cost_center = None
|
||||
target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
|
||||
|
||||
doclist = get_mapped_doc("Quotation", source_name, {
|
||||
|
||||
@@ -774,7 +774,7 @@ class POSCart {
|
||||
});
|
||||
this.numpad.reset_value();
|
||||
} else {
|
||||
const item_code = this.selected_item.attr('data-item-code');
|
||||
const item_code = unescape(this.selected_item.attr('data-item-code'));
|
||||
const field = this.selected_item.active_field;
|
||||
const value = this.numpad.get_value();
|
||||
|
||||
@@ -819,7 +819,7 @@ class POSCart {
|
||||
}
|
||||
|
||||
update_item(item) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item.item_code)}"]`);
|
||||
|
||||
if(item.qty > 0) {
|
||||
const is_stock_item = this.get_item_details(item.item_code).is_stock_item;
|
||||
@@ -841,7 +841,7 @@ class POSCart {
|
||||
const rate = format_currency(item.rate, this.frm.doc.currency);
|
||||
const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red';
|
||||
return `
|
||||
<div class="list-item indicator ${indicator_class}" data-item-code="${item.item_code}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
|
||||
<div class="list-item indicator ${indicator_class}" data-item-code="${escape(item.item_code)}" title="Item: ${item.item_name} Available Qty: ${item.actual_qty}">
|
||||
<div class="item-name list-item__content list-item__content--flex-1.5 ellipsis">
|
||||
${item.item_name}
|
||||
</div>
|
||||
@@ -883,18 +883,18 @@ class POSCart {
|
||||
}
|
||||
|
||||
exists(item_code) {
|
||||
let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
let $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
return $item.length > 0;
|
||||
}
|
||||
|
||||
highlight_item(item_code) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
$item.addClass('highlight');
|
||||
setTimeout(() => $item.removeClass('highlight'), 1000);
|
||||
}
|
||||
|
||||
scroll_to_item(item_code) {
|
||||
const $item = this.$cart_items.find(`[data-item-code="${item_code}"]`);
|
||||
const $item = this.$cart_items.find(`[data-item-code="${escape(item_code)}"]`);
|
||||
if ($item.length === 0) return;
|
||||
const scrollTop = $item.offset().top - this.$cart_items.offset().top + this.$cart_items.scrollTop();
|
||||
this.$cart_items.animate({ scrollTop });
|
||||
@@ -909,7 +909,7 @@ class POSCart {
|
||||
'[data-action="increment"], [data-action="decrement"]', function() {
|
||||
const $btn = $(this);
|
||||
const $item = $btn.closest('.list-item[data-item-code]');
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
const action = $btn.attr('data-action');
|
||||
|
||||
if(action === 'increment') {
|
||||
@@ -932,7 +932,7 @@ class POSCart {
|
||||
this.$cart_items.on('change', '.quantity input', function() {
|
||||
const $input = $(this);
|
||||
const $item = $input.closest('.list-item[data-item-code]');
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
events.on_field_change(item_code, 'qty', flt($input.val()));
|
||||
});
|
||||
|
||||
@@ -1200,7 +1200,7 @@ class POSItems {
|
||||
var me = this;
|
||||
this.wrapper.on('click', '.pos-item-wrapper', function() {
|
||||
const $item = $(this);
|
||||
const item_code = $item.attr('data-item-code');
|
||||
const item_code = unescape($item.attr('data-item-code'));
|
||||
me.events.update_cart(item_code, 'qty', '+1');
|
||||
});
|
||||
}
|
||||
@@ -1226,7 +1226,7 @@ class POSItems {
|
||||
const item_title = item_name || item_code;
|
||||
|
||||
const template = `
|
||||
<div class="pos-item-wrapper image-view-item" data-item-code="${item_code}">
|
||||
<div class="pos-item-wrapper image-view-item" data-item-code="${escape(item_code)}">
|
||||
<div class="image-view-header">
|
||||
<div>
|
||||
<a class="grey list-id" data-name="${item_code}" title="${item_title}">
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:08:44.614482",
|
||||
"modified": "2018-06-18 16:42:05.860309",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Item-wise Sales History",
|
||||
"owner": "Administrator",
|
||||
"query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n so_item.item_group as \"Item Group:Link/Item Group:120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Float:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.base_rate as \"Rate:Currency:120\",\n\tso_item.base_amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n cu.customer_name as \"Customer Name::150\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\",\n\tso.company as \"Company:Link/Company:\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item, `tabCustomer` cu\nwhere\n\tso.name = so_item.parent and so.customer=cu.name\n\tand so.docstatus = 1\norder by so.name desc",
|
||||
"query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n so_item.item_group as \"Item Group:Link/Item Group:120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Data:100\",\n\tso_item.uom as \"UOM:Link/UOM:80\",\n\tso_item.base_rate as \"Rate:Currency:120\",\n\tso_item.base_amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n cu.customer_name as \"Customer Name::150\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\",\n\tso.company as \"Company:Link/Company:\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item, `tabCustomer` cu\nwhere\n\tso.name = so_item.parent and so.customer=cu.name\n\tand so.docstatus = 1\norder by so.name desc",
|
||||
"ref_doctype": "Sales Order",
|
||||
"report_name": "Item-wise Sales History",
|
||||
"report_type": "Query Report",
|
||||
|
||||
@@ -91,17 +91,22 @@ def get_achieved_details(filters, sales_person, all_sales_persons, target_item_g
|
||||
start_date, end_date = get_fiscal_year(fiscal_year = filters["fiscal_year"])[1:]
|
||||
|
||||
item_details = frappe.db.sql("""
|
||||
select
|
||||
sum(soi.stock_qty * (st.allocated_percentage/100)) as qty,
|
||||
sum(soi.base_net_amount * (st.allocated_percentage/100)) as amount,
|
||||
st.sales_person, MONTHNAME(so.transaction_date) as month_name
|
||||
SELECT st.sales_person, MONTHNAME(so.transaction_date) as month_name,
|
||||
CASE
|
||||
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * (st.allocated_percentage/100))
|
||||
ELSE sum(soi.stock_qty * (st.allocated_percentage/100))
|
||||
END as qty,
|
||||
CASE
|
||||
WHEN so.status = "Closed" THEN sum(soi.delivered_qty * soi.conversion_factor * soi.base_net_rate * (st.allocated_percentage/100))
|
||||
ELSE sum(soi.base_net_amount * (st.allocated_percentage/100))
|
||||
END as amount
|
||||
from
|
||||
`tabSales Order Item` soi, `tabSales Order` so, `tabSales Team` st
|
||||
where
|
||||
soi.parent=so.name and so.docstatus=1 and st.parent=so.name
|
||||
and so.transaction_date>=%s and so.transaction_date<=%s
|
||||
and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name=st.sales_person)
|
||||
and exists(select name from `tabItem Group` where lft >= %s and rgt <= %s and name=soi.item_group)
|
||||
and exists(SELECT name from `tabSales Person` where lft >= %s and rgt <= %s and name=st.sales_person)
|
||||
and exists(SELECT name from `tabItem Group` where lft >= %s and rgt <= %s and name=soi.item_group)
|
||||
group by
|
||||
sales_person, month_name
|
||||
""",
|
||||
|
||||
@@ -15,11 +15,12 @@ def execute(filters=None):
|
||||
data = []
|
||||
|
||||
for d in entries:
|
||||
data.append([
|
||||
d.name, d.customer, d.territory, d.posting_date, d.item_code,
|
||||
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
|
||||
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
|
||||
])
|
||||
if d.stock_qty > 0:
|
||||
data.append([
|
||||
d.name, d.customer, d.territory, d.posting_date, d.item_code,
|
||||
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),
|
||||
d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt
|
||||
])
|
||||
|
||||
if data:
|
||||
total_row = [""]*len(data[0])
|
||||
@@ -40,18 +41,34 @@ def get_columns(filters):
|
||||
|
||||
def get_entries(filters):
|
||||
date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date"
|
||||
if filters["doc_type"] == "Sales Order":
|
||||
qty_field = "delivered_qty"
|
||||
else:
|
||||
qty_field = "qty"
|
||||
conditions, values = get_conditions(filters, date_field)
|
||||
|
||||
entries = frappe.db.sql("""
|
||||
select
|
||||
dt.name, dt.customer, dt.territory, dt.%s as posting_date, dt_item.item_code,
|
||||
dt_item.stock_qty, dt_item.base_net_amount, st.sales_person, st.allocated_percentage,
|
||||
dt_item.base_net_amount*st.allocated_percentage/100 as contribution_amt
|
||||
st.sales_person, st.allocated_percentage,
|
||||
CASE
|
||||
WHEN dt.status = "Closed" THEN dt_item.%s * dt_item.conversion_factor
|
||||
ELSE dt_item.stock_qty
|
||||
END as stock_qty,
|
||||
CASE
|
||||
WHEN dt.status = "Closed" THEN (dt_item.base_net_rate * dt_item.%s * dt_item.conversion_factor)
|
||||
ELSE dt_item.base_net_amount
|
||||
END as base_net_amount,
|
||||
CASE
|
||||
WHEN dt.status = "Closed" THEN ((dt_item.base_net_rate * dt_item.%s * dt_item.conversion_factor) * st.allocated_percentage/100)
|
||||
ELSE dt_item.base_net_amount * st.allocated_percentage/100
|
||||
END as contribution_amt
|
||||
from
|
||||
`tab%s` dt, `tab%s Item` dt_item, `tabSales Team` st
|
||||
where
|
||||
st.parent = dt.name and dt.name = dt_item.parent and st.parenttype = %s
|
||||
and dt.docstatus = 1 %s order by st.sales_person, dt.name desc
|
||||
""" %(date_field, filters["doc_type"], filters["doc_type"], '%s', conditions),
|
||||
""" %(date_field, qty_field, qty_field, qty_field, filters["doc_type"], filters["doc_type"], '%s', conditions),
|
||||
tuple([filters["doc_type"]] + values), as_dict=1)
|
||||
|
||||
return entries
|
||||
|
||||
@@ -40,7 +40,7 @@ class AuthorizationControl(TransactionBase):
|
||||
chk = 1
|
||||
add_cond1,add_cond2 = '',''
|
||||
if based_on == 'Itemwise Discount':
|
||||
add_cond1 += " and master_name = '"+cstr(item).replace("'", "\\'")+"'"
|
||||
add_cond1 += " and master_name = '"+frappe.db.escape(cstr(item))+"'"
|
||||
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
|
||||
where transaction = %s and value <= %s
|
||||
and based_on = %s and company = %s and docstatus != 2 %s %s""" %
|
||||
|
||||
@@ -122,6 +122,7 @@ class Company(Document):
|
||||
if not wh_detail["is_group"] else ""
|
||||
})
|
||||
warehouse.flags.ignore_permissions = True
|
||||
warehouse.flags.ignore_mandatory = True
|
||||
warehouse.insert()
|
||||
|
||||
def create_default_accounts(self):
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
import frappe, unittest
|
||||
from frappe.utils import flt
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
|
||||
test_records = frappe.get_test_records('Currency Exchange')
|
||||
@@ -44,7 +45,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
|
||||
# Start with allow_stale is True
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
|
||||
self.assertEqual(exchange_rate, 60.0)
|
||||
self.assertEqual(flt(exchange_rate, 3), 60.0)
|
||||
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||
self.assertEqual(exchange_rate, 65.1)
|
||||
@@ -56,7 +57,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
|
||||
self.assertFalse(exchange_rate == 60)
|
||||
self.assertEqual(exchange_rate, 66.894)
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.894)
|
||||
|
||||
def test_exchange_rate_strict(self):
|
||||
# strict currency settings
|
||||
@@ -69,7 +70,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
# Will fetch from fixer.io
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||
self.assertEqual(exchange_rate, 67.79)
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.79)
|
||||
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30")
|
||||
self.assertEqual(exchange_rate, 62.9)
|
||||
@@ -77,7 +78,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
|
||||
self.assertEqual(exchange_rate, 66.894)
|
||||
self.assertEqual(flt(exchange_rate, 3), 66.894)
|
||||
|
||||
exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10")
|
||||
self.assertEqual(exchange_rate, 65.1)
|
||||
@@ -100,4 +101,4 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
# Will fetch from fixer.io
|
||||
self.clear_cache()
|
||||
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
|
||||
self.assertEqual(exchange_rate, 67.79)
|
||||
self.assertEqual(flt(exchange_rate, 3), 67.79)
|
||||
@@ -113,8 +113,7 @@ def create_logo(args):
|
||||
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name") ))
|
||||
|
||||
def create_website(args):
|
||||
if args.get('setup_website'):
|
||||
website_maker(args)
|
||||
website_maker(args)
|
||||
|
||||
def get_fy_details(fy_start_date, fy_end_date):
|
||||
start_year = getdate(fy_start_date).year
|
||||
|
||||
@@ -12,7 +12,7 @@ class website_maker(object):
|
||||
self.args = args
|
||||
self.company = args.company_name
|
||||
self.tagline = args.company_tagline
|
||||
self.user = args.name
|
||||
self.user = args.get('email')
|
||||
self.make_web_page()
|
||||
self.make_website_settings()
|
||||
self.make_blog()
|
||||
@@ -50,6 +50,17 @@ class website_maker(object):
|
||||
website_settings.save()
|
||||
|
||||
def make_blog(self):
|
||||
blog_category = frappe.get_doc({
|
||||
"doctype": "Blog Category",
|
||||
"category_name": "general",
|
||||
"published": 1,
|
||||
"title": _("General")
|
||||
}).insert()
|
||||
|
||||
if not self.user:
|
||||
# Admin setup
|
||||
return
|
||||
|
||||
blogger = frappe.new_doc("Blogger")
|
||||
user = frappe.get_doc("User", self.user)
|
||||
blogger.user = self.user
|
||||
@@ -58,13 +69,6 @@ class website_maker(object):
|
||||
blogger.avatar = user.user_image
|
||||
blogger.insert()
|
||||
|
||||
blog_category = frappe.get_doc({
|
||||
"doctype": "Blog Category",
|
||||
"category_name": "general",
|
||||
"published": 1,
|
||||
"title": _("General")
|
||||
}).insert()
|
||||
|
||||
frappe.get_doc({
|
||||
"doctype": "Blog Post",
|
||||
"title": "Welcome",
|
||||
|
||||
@@ -37,6 +37,7 @@ def set_default_settings(args):
|
||||
stock_settings.auto_indent = 1
|
||||
stock_settings.auto_insert_price_list_rate_if_missing = 1
|
||||
stock_settings.automatically_set_serial_nos_based_on_fifo = 1
|
||||
stock_settings.set_qty_in_transactions_based_on_serial_no_input = 1
|
||||
stock_settings.save()
|
||||
|
||||
selling_settings = frappe.get_doc("Selling Settings")
|
||||
|
||||
@@ -95,7 +95,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None):
|
||||
|
||||
if not value:
|
||||
import requests
|
||||
api_url = "http://api.fixer.io/{0}".format(transaction_date)
|
||||
api_url = "https://exchangeratesapi.io/api/{0}".format(transaction_date)
|
||||
response = requests.get(api_url, params={
|
||||
"base": from_currency,
|
||||
"symbols": to_currency
|
||||
|
||||
@@ -3157,6 +3157,36 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
@@ -3701,7 +3731,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2018-03-13 15:35:02.234116",
|
||||
"modified": "2018-05-28 03:03:35.035396",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
||||
@@ -82,6 +82,8 @@ class DeliveryNote(SellingController):
|
||||
for f in fieldname:
|
||||
toggle_print_hide(self.meta if key == "parent" else item_meta, f)
|
||||
|
||||
super(DeliveryNote, self).before_print()
|
||||
|
||||
def set_actual_qty(self):
|
||||
for d in self.get('items'):
|
||||
if d.item_code and d.warehouse:
|
||||
|
||||
@@ -501,7 +501,7 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def update_item_price(self):
|
||||
frappe.db.sql("""update `tabItem Price` set item_name=%s,
|
||||
item_description=%s, modified=NOW() where item_code=%s""",
|
||||
item_description=%s where item_code=%s""",
|
||||
(self.item_name, self.description, self.name))
|
||||
|
||||
def on_trash(self):
|
||||
@@ -604,19 +604,20 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def update_template_item(self):
|
||||
"""Set Show in Website for Template Item if True for its Variant"""
|
||||
if self.variant_of and self.show_in_website:
|
||||
self.show_variant_in_website = 1
|
||||
self.show_in_website = 0
|
||||
if self.variant_of:
|
||||
if self.show_in_website:
|
||||
self.show_variant_in_website = 1
|
||||
self.show_in_website = 0
|
||||
|
||||
if self.show_variant_in_website:
|
||||
# show template
|
||||
template_item = frappe.get_doc("Item", self.variant_of)
|
||||
if self.show_variant_in_website:
|
||||
# show template
|
||||
template_item = frappe.get_doc("Item", self.variant_of)
|
||||
|
||||
if not template_item.show_in_website:
|
||||
template_item.show_in_website = 1
|
||||
template_item.flags.dont_update_variants = True
|
||||
template_item.flags.ignore_permissions = True
|
||||
template_item.save()
|
||||
if not template_item.show_in_website:
|
||||
template_item.show_in_website = 1
|
||||
template_item.flags.dont_update_variants = True
|
||||
template_item.flags.ignore_permissions = True
|
||||
template_item.save()
|
||||
|
||||
def update_variants(self):
|
||||
if self.flags.dont_update_variants or \
|
||||
@@ -640,9 +641,9 @@ class Item(WebsiteGenerator):
|
||||
|
||||
def validate_stock_exists_for_template_item(self):
|
||||
if self.stock_ledger_created() and self._doc_before_save:
|
||||
if (self._doc_before_save.has_variants != self.has_variants
|
||||
if (cint(self._doc_before_save.has_variants) != cint(self.has_variants)
|
||||
or self._doc_before_save.variant_of != self.variant_of):
|
||||
frappe.throw(_("Cannot change Variant properties after stock transction. You will have to make a new Item to do this.").format(self.name),
|
||||
frappe.throw(_("Cannot change Variant properties after stock transaction. You will have to make a new Item to do this.").format(self.name),
|
||||
StockExistsForTemplate)
|
||||
|
||||
if self.has_variants or self.variant_of:
|
||||
|
||||
@@ -417,6 +417,7 @@ def raise_production_orders(material_request):
|
||||
prod_order.material_request_item = d.name
|
||||
prod_order.planned_start_date = mr.transaction_date
|
||||
prod_order.company = mr.company
|
||||
prod_order.set_production_order_operations()
|
||||
prod_order.save()
|
||||
production_orders.append(prod_order.name)
|
||||
else:
|
||||
|
||||
@@ -7,7 +7,7 @@ def get_data():
|
||||
'transactions': [
|
||||
{
|
||||
'label': _('Related'),
|
||||
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order']
|
||||
'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', "Stock Entry"]
|
||||
},
|
||||
{
|
||||
'label': _('Manufacturing'),
|
||||
|
||||
@@ -49,7 +49,8 @@ def update_packing_list_item(doc, packing_item_code, qty, main_item_row, descrip
|
||||
pi.qty = flt(qty)
|
||||
pi.description = description
|
||||
if not pi.warehouse:
|
||||
pi.warehouse = item.default_warehouse or main_item_row.warehouse
|
||||
pi.warehouse = (main_item_row.warehouse
|
||||
if (doc.get('is_pos') or not item.default_warehouse) else item.default_warehouse)
|
||||
if not pi.batch_no:
|
||||
pi.batch_no = cstr(main_item_row.get("batch_no"))
|
||||
if not pi.target_warehouse:
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
"label": "Item Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "item_code.item_name",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
@@ -435,7 +436,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-02-20 13:30:26.818408",
|
||||
"modified": "2018-06-01 07:21:58.220980",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Packing Slip Item",
|
||||
|
||||
@@ -2978,10 +2978,69 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "group_same_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Group same items",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_97",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "other_details",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 1,
|
||||
@@ -3242,7 +3301,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-11-29 14:15:10.309900",
|
||||
"modified": "2018-05-28 02:59:59.609643",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
||||
@@ -120,6 +120,8 @@ class PurchaseReceipt(BuyingController):
|
||||
self.update_prevdoc_status()
|
||||
if self.per_billed < 100:
|
||||
self.update_billing_status()
|
||||
else:
|
||||
self.status = "Completed"
|
||||
|
||||
# Updating stock ledger should always be called after updating prevdoc status,
|
||||
# because updating ordered qty, reserved_qty_for_subcontract in bin
|
||||
|
||||
@@ -198,7 +198,7 @@ def validate_serial_no(sle, item_det):
|
||||
frappe.throw(_("Serial No {0} quantity {1} cannot be a fraction").format(sle.item_code, sle.actual_qty))
|
||||
|
||||
if len(serial_nos) and len(serial_nos) != abs(cint(sle.actual_qty)):
|
||||
frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(sle.actual_qty, sle.item_code, len(serial_nos)),
|
||||
frappe.throw(_("{0} Serial Numbers required for Item {1}. You have provided {2}.").format(abs(sle.actual_qty), sle.item_code, len(serial_nos)),
|
||||
SerialNoQtyError)
|
||||
|
||||
if len(serial_nos) != len(set(serial_nos)):
|
||||
|
||||
@@ -263,7 +263,7 @@ class StockEntry(StockController):
|
||||
d.actual_qty = previous_sle.get("qty_after_transaction") or 0
|
||||
|
||||
# validate qty during submit
|
||||
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and d.actual_qty < d.transfer_qty:
|
||||
if d.docstatus==1 and d.s_warehouse and not allow_negative_stock and flt(d.actual_qty, d.precision("actual_qty")) < flt(d.transfer_qty, d.precision("actual_qty")):
|
||||
frappe.throw(_("Row {0}: Qty not available for {4} in warehouse {1} at posting time of the entry ({2} {3})").format(d.idx,
|
||||
frappe.bold(d.s_warehouse), formatdate(self.posting_date),
|
||||
format_time(self.posting_time), frappe.bold(d.item_code))
|
||||
|
||||
@@ -77,8 +77,8 @@ class StockLedgerEntry(Document):
|
||||
elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}):
|
||||
frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, self.item_code))
|
||||
|
||||
elif item_det.has_batch_no ==0 and self.batch_no:
|
||||
frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
|
||||
elif item_det.has_batch_no ==0 and self.batch_no and self.is_cancelled == "No":
|
||||
frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code))
|
||||
|
||||
if item_det.has_variants:
|
||||
frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
|
||||
|
||||
@@ -463,6 +463,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "set_qty_in_transactions_based_on_serial_no_input",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Set Qty in Transactions based on Serial No Input",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
@@ -679,7 +710,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-11-17 01:35:49.562613",
|
||||
"modified": "2018-05-03 12:37:12.905394",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Settings",
|
||||
|
||||
@@ -11,8 +11,9 @@ from frappe.utils.html_utils import clean_html
|
||||
|
||||
class StockSettings(Document):
|
||||
def validate(self):
|
||||
for key in ["item_naming_by", "item_group", "stock_uom", "allow_negative_stock", "default_warehouse"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
for key in ["item_naming_by", "item_group", "stock_uom",
|
||||
"allow_negative_stock", "default_warehouse", "set_qty_in_transactions_based_on_serial_no_input"]:
|
||||
frappe.db.set_default(key, self.get(key, ""))
|
||||
|
||||
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
|
||||
set_by_naming_series("Item", "item_code",
|
||||
|
||||
@@ -39,6 +39,8 @@ frappe.ui.form.on("Warehouse", {
|
||||
|
||||
frm.toggle_enable(['is_group', 'company'], false);
|
||||
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Warehouse'};
|
||||
|
||||
frm.fields_dict['parent_warehouse'].get_query = function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
|
||||
@@ -14,6 +14,19 @@ frappe.query_reports["Itemwise Recommended Reorder Level"] = {
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.datetime.get_today()
|
||||
},
|
||||
{
|
||||
"fieldname":"item_group",
|
||||
"label": __("Item Group"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item Group",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"brand",
|
||||
"label": __("Brand"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Brand"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10,7 +10,7 @@ def execute(filters=None):
|
||||
if not filters: filters = {}
|
||||
float_preceision = frappe.db.get_default("float_preceision")
|
||||
|
||||
condition =get_condition(filters)
|
||||
condition = get_condition(filters)
|
||||
|
||||
avg_daily_outgoing = 0
|
||||
diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days)+1
|
||||
@@ -18,7 +18,7 @@ def execute(filters=None):
|
||||
frappe.throw(_("'From Date' must be after 'To Date'"))
|
||||
|
||||
columns = get_columns()
|
||||
items = get_item_info()
|
||||
items = get_item_info(filters)
|
||||
consumed_item_map = get_consumed_items(condition)
|
||||
delivered_item_map = get_delivered_items(condition)
|
||||
|
||||
@@ -28,23 +28,31 @@ def execute(filters=None):
|
||||
avg_daily_outgoing = flt(total_outgoing / diff, float_preceision)
|
||||
reorder_level = (avg_daily_outgoing * flt(item.lead_time_days)) + flt(item.safety_stock)
|
||||
|
||||
data.append([item.name, item.item_name, item.description, item.safety_stock, item.lead_time_days,
|
||||
consumed_item_map.get(item.name, 0), delivered_item_map.get(item.name,0), total_outgoing,
|
||||
avg_daily_outgoing, reorder_level])
|
||||
data.append([item.name, item.item_name, item.item_group, item.brand, item.description,
|
||||
item.safety_stock, item.lead_time_days, consumed_item_map.get(item.name, 0),
|
||||
delivered_item_map.get(item.name,0), total_outgoing, avg_daily_outgoing, reorder_level])
|
||||
|
||||
return columns , data
|
||||
|
||||
def get_columns():
|
||||
return[
|
||||
_("Item") + ":Link/Item:120", _("Item Name") + ":Data:120", _("Description") + "::160",
|
||||
_("Item") + ":Link/Item:120", _("Item Name") + ":Data:120", _("Item Group") + ":Link/Item Group:100",
|
||||
_("Brand") + ":Link/Brand:100", _("Description") + "::160",
|
||||
_("Safety Stock") + ":Float:160", _("Lead Time Days") + ":Float:120", _("Consumed") + ":Float:120",
|
||||
_("Delivered") + ":Float:120", _("Total Outgoing") + ":Float:120", _("Avg Daily Outgoing") + ":Float:160",
|
||||
_("Reorder Level") + ":Float:120"
|
||||
]
|
||||
|
||||
def get_item_info():
|
||||
return frappe.db.sql("""select name, item_name, description, safety_stock,
|
||||
lead_time_days from tabItem""", as_dict=1)
|
||||
def get_item_info(filters):
|
||||
from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition
|
||||
conditions = [get_item_group_condition(filters.get("item_group"))]
|
||||
if filters.get("brand"):
|
||||
conditions.append("item.brand=%(brand)s")
|
||||
|
||||
return frappe.db.sql("""select name, item_name, description, brand, item_group,
|
||||
safety_stock, lead_time_days from `tabItem` item where {}"""
|
||||
.format(" and ".join(conditions)), filters, as_dict=1)
|
||||
|
||||
|
||||
def get_consumed_items(condition):
|
||||
cn_items = frappe.db.sql("""select se_item.item_code,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user