Added Number Cards for Opportunities
This commit is contained in:
12
rangeldigital/fixtures/item_custom_fields.json
Normal file
12
rangeldigital/fixtures/item_custom_fields.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
|
||||||
|
"doctype": "Custom Field",
|
||||||
|
"dt": "Item",
|
||||||
|
"fieldname": "manuf_item_code",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"insert_after": "stock_uom",
|
||||||
|
"label": "Manufacturer Item Code",
|
||||||
|
"name": "item_manuf_item_code"
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -7,8 +7,8 @@
|
|||||||
"doc_type": "Quotation",
|
"doc_type": "Quotation",
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Print Format",
|
"doctype": "Print Format",
|
||||||
"html": "<table width=\"100%\" class=\"header\" ><tr><td style=\"width: 50%; vertical-align: top; padding: 10px;\"><h2 class=\"font-color-3 font-size-4 bold\">Rangel Digital LLC</h2><p>sales@rangeldigital.com</p><p>541-808-9102</p></td><td class=\"details\" style=\"width: 50%; vertical-align: top; padding: 10px;\"><h2 class=\"quote-heading bold text-right\">QUOTE</h2><table width=\"100%\" class=\"details\" ><tr><td style=\"width: 25%; vertical-align: top; padding:0!important;\"><p class=\"bold\">Quote #</p><p class=\"bold\">Quote Date</p>{% if doc.valid_till %}<p class=\"bold\">Expires</p> {% endif %}</td><td style=\"width: 70%; vertical-align: top; padding:0!important;\"><p class=\"text-right\">{{ doc.name }}</p><p style=\"text-align: right;\">{{ frappe.utils.formatdate(doc.transaction_date, \"MMMM d, YYYY\") }}</p>{% if doc.valid_till %}<p class=\"text-right\">{{ frappe.utils.formatdate(doc.valid_till, \"MMMM d, yyyy\") }}</p>{% endif %}</td></tr></table></td></tr></table><table width=\"100%\" class=\"addresses\" ><tr><td style=\"width: 50%; vertical-align: top; padding: 10px;\"><p class=\"bill-to-label bold font-size-2\">Bill To</p><p class=\"party-name\">{{doc.party_name }}</p><p class=\"bill-to-address\"> {% if doc.address_display%}{{ doc.address_display}}{% endif %}</p></td><td style=\"width: 50%; vertical-align: top; padding: 10px;\">{% if doc.shipping_address%}<p class=\"bill-to-label bold font-size-2\">Deliver To</p><p class=\"party-name\">{{doc.party_name }}</p><p class=\"bill-to-address\"> {{ doc.shipping_address}}</p>{% endif %}</td></tr></table></td></tr></table><table class=\"items-table\" width=\"100%\" border=\"0\"><thead><tr class=\"header-row\"><th class=\"item-title text-left\">Item</th><th class=\"quantity-title text-center\">Quantity</th><th class=\"price-title text-center\">Price</th><th class=\"total-title text-right\">Total</th></tr></thead><tbody>{% for item in doc.items %}<tr class=\"row\"><td class=\"item-name bold text-left\">{{ item.item_name }}</td><td class=\"item-quantity text-center\">{{ item.qty }}</td><td class=\"item-rate text-center\">${{ \"{:,.2f}\".format(item.rate) }}</td><td class=\"item-amount text-right\">${{ \"{:,.2f}\".format(item.amount) }}</td></tr>{% if item.description and item.description != item.item_name %}<tr class=\"row description-row\"><td colspan=\"4\" class=\"description-cell\">{{ item.description }}</td></tr>{% endif %}{% endfor %}</tbody></table><div class=\"totals\"><table width=\"250px\" style=\"margin-left:auto;margin-right:0\" class=\"\" ><tr><td class=\"bold\">Sub-Total:</td><td class=\"text-right\"> ${{ \"{:,.2f}\".format(doc.base_total) }}</td></tr><tr><td class=\"bold\">Taxes: </td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.total_taxes_and_charges) }}</td></tr><td class=\"bold\">Discount:</td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.discount_amount) }}</td></tr><tr class=\"grand-total\"><td class=\"bold\">Grand Total:</td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.grand_total) }}</td></tr></table></div>{% if doc.terms %}<div class=\"terms-section\"><h3 class=\"terms-conditions bold\">Terms and Conditions</h3><p>{{doc.terms}}</p></div>{% endif %}",
|
"html": "<table width=\"100%\" class=\"header\" ><tr><td style=\"width: 50%; vertical-align: top; padding: 10px;\"><h2 class=\"font-color-3 font-size-4 bold\">Rangel Digital LLC</h2><p>sales@rangeldigital.com</p><p>541-808-9102</p></td><td class=\"details\" style=\"width: 50%; vertical-align: top; padding: 10px;\"><h2 class=\"quote-heading bold text-right\">QUOTE</h2><table width=\"100%\" class=\"details\" ><tr><td style=\"width: 25%; vertical-align: top; padding:0!important;\"><p class=\"bold\">Quote #</p><p class=\"bold\">Quote Date</p>{% if doc.valid_till %}<p class=\"bold\">Expires</p> {% endif %}</td><td style=\"width: 70%; vertical-align: top; padding:0!important;\"><p class=\"text-right\">{{ doc.name }}</p><p style=\"text-align: right;\">{{ frappe.utils.formatdate(doc.transaction_date, \"MMMM d, YYYY\") }}</p>{% if doc.valid_till %}<p class=\"text-right\">{{ frappe.utils.formatdate(doc.valid_till, \"MMMM d, yyyy\") }}</p>{% endif %}</td></tr></table></td></tr></table><table width=\"100%\" class=\"addresses\" ><tr><td style=\"width: 50%; vertical-align: top; padding: 10px;\"><p class=\"bill-to-label bold font-size-2\">Bill To</p><p class=\"party-name\">{{doc.party_name }}</p><p class=\"bill-to-address\"> {% if doc.address_display%}{{ doc.address_display}}{% endif %}</p></td><td style=\"width: 50%; vertical-align: top; padding: 10px;\">{% if doc.shipping_address%}<p class=\"bill-to-label bold font-size-2\">Deliver To</p><p class=\"party-name\">{{doc.party_name }}</p><p class=\"bill-to-address\"> {{ doc.shipping_address}}</p>{% endif %}</td></tr></table></td></tr></table><table class=\"items-table\" width=\"100%\" border=\"0\"><thead><tr class=\"header-row\"><th class=\"item-title text-left\">Item</th><th class=\"quantity-title text-center\">Quantity</th><th class=\"price-title text-center\">Price</th><th class=\"total-title text-right\">Total</th></tr></thead><tbody>{% for item in doc.items %}<tr class=\"row\"><td class=\"item-name text-left\"><span><span class=\"bold\">{{ item.item_name }}</span>{% if item.manuf_item_code %} ({{ item.manuf_item_code }}){% endif %}</span></td><td class=\"item-quantity text-center\">{{ item.qty }}</td><td class=\"item-rate text-center\">${{ \"{:,.2f}\".format(item.rate) }}</td><td class=\"item-amount text-right\">${{ \"{:,.2f}\".format(item.amount) }}</td></tr>{% if item.description and item.description != item.item_name %}<tr class=\"row description-row\"><td colspan=\"4\" class=\"description-cell\">{{ item.description }}</td></tr>{% endif %}{% endfor %}</tbody></table><div class=\"totals\"><table width=\"250px\" style=\"margin-left:auto;margin-right:0\" class=\"\" ><tr><td class=\"bold\">Sub-Total:</td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.base_total) }}</td></tr><tr><td class=\"bold\">Taxes: </td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.total_taxes_and_charges) }}</td></tr><td class=\"bold\">Discount:</td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.discount_amount) }}</td></tr><tr class=\"grand-total\"><td class=\"bold\">Grand Total:</td><td class=\"text-right\">${{ \"{:,.2f}\".format(doc.grand_total) }}</td></tr></table></div>{% if doc.terms %}<div class=\"terms-section\"><h3 class=\"terms-conditions bold\">Terms and Conditions</h3><p>{{doc.terms}}</p></div>{% endif %}"
|
||||||
"css": ".bold {font-weight:bold} .font-size-1 {font-size:14px!important;} .font-size-2 {font-size:16px!important} .font-size-4 {font-size:24px!important;} .font-color-3 {color:#008AFC!important} .text-left {text-align:left!important;} .text-center {text-align:center!important;} .text-right {text-align:right!important} table {border-spacing: 0 1px;border-collapse:separate} p,td {font-size:14px;} h2 {margin:0!important;} h2.quote-heading {font-size:20px;} .header {margin-bottom:40px;} .print-format td.details {padding-left:20px!important} .addresses {margin-bottom:40px;} .items-table *{line-height:16px} .items-table .row:before {display:none!important;} .items-table th, .items-table td {font-size:14px;} .items-table .header-row {background:#02204B} .items-table .header-row td {font-weight:bold} .items-table .header-row th {color:white;font-weight:bold} .items-table .row:not(.description-row) td {padding-top:5px!important;padding-bottom:0!important;} .items-table .description-row td {padding-top:0!important;padding-bottom:0!important;} .description-row p {padding-left:5px!important} .totals {margin-top:50px;font-size:14px} .totals td {padding-top:3px!important;padding-right:10px!important;padding-bottom:3px!important;} .totals .grand-total {background:#f1f1f1;} .totals .grand-total td{padding-top:7px!important;padding-bottom:7px!important;} .terms-section {margin-top:40px;} .terms-section h3 {margin-bottom:5px;}",
|
, "css": ".bold {font-weight:bold} .font-size-1 {font-size:14px!important;} .font-size-2 {font-size:16px!important} .font-size-4 {font-size:24px!important;} .font-color-3 {color:#008AFC!important} .text-left {text-align:left!important;} .text-center {text-align:center!important;} .text-right {text-align:right!important} table {border-spacing: 0 1px;border-collapse:separate} p,td {font-size:14px;} h2 {margin:0!important;} h2.quote-heading {font-size:20px;} .header {margin-bottom:40px;} .print-format td.details {padding-left:20px!important} .addresses {margin-bottom:40px;} .items-table *{line-height:16px} .items-table .row:before {display:none!important;} .items-table th, .items-table td {font-size:14px;} .items-table .header-row {background:#02204B} .items-table .header-row td {font-weight:bold} .items-table .header-row th {color:white;font-weight:bold} .items-table .row:not(.description-row) td {padding-top:5px!important;padding-bottom:0!important;} .items-table .description-row td {padding-top:0!important;padding-bottom:0!important;} .description-row p {padding-left:5px!important} .totals {margin-top:50px;font-size:14px} .totals td {padding-top:3px!important;padding-right:10px!important;padding-bottom:3px!important;} .totals .grand-total {background:#f1f1f1;} .totals .grand-total td{padding-top:7px!important;padding-bottom:7px!important;} .terms-section {margin-top:40px;} .terms-section h3 {margin-bottom:5px;}",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"line_breaks": 0,
|
"line_breaks": 0,
|
||||||
"is_default": 1,
|
"is_default": 1,
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
|
||||||
{
|
{
|
||||||
"doctype": "Property Setter",
|
"doctype": "Property Setter",
|
||||||
"name": "Lead-status_options",
|
"name": "Lead-status_options",
|
||||||
|
|||||||
14
rangeldigital/fixtures/quotation_item_custom_fields.json
Normal file
14
rangeldigital/fixtures/quotation_item_custom_fields.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
|
||||||
|
"doctype": "Custom Field",
|
||||||
|
"dt": "Quotation Item",
|
||||||
|
"fetch_from": "item_code.manuf_item_code",
|
||||||
|
"fieldname": "manuf_item_code",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"insert_after": "item_code",
|
||||||
|
"label": "Manufacturer Item Code",
|
||||||
|
"name": "quotation_item_manuf_item_code",
|
||||||
|
"readonly": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
13
rangeldigital/fixtures/sales_stage_custom_fields.json
Normal file
13
rangeldigital/fixtures/sales_stage_custom_fields.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
|
||||||
|
"doctype": "Custom Field",
|
||||||
|
"dt": "Sales Stage",
|
||||||
|
"fieldname": "opportunity_probability",
|
||||||
|
"fieldtype": "Percent",
|
||||||
|
"insert_after": "",
|
||||||
|
"label": "Probability",
|
||||||
|
"name": "sales_stage_opportunity_probability",
|
||||||
|
"readonly": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,9 +1,15 @@
|
|||||||
/* VARIABLES */
|
/* VARIABLES */
|
||||||
:root {
|
:root {
|
||||||
|
--primary: white;
|
||||||
--text-color: white;
|
--text-color: white;
|
||||||
|
--border-color: none;
|
||||||
|
--darker-border-color: none;
|
||||||
--fg-color: #1D1F37; /* Dark Bluish Purple */
|
--fg-color: #1D1F37; /* Dark Bluish Purple */
|
||||||
--gray-900: white;
|
--gray-900: white;
|
||||||
|
--gray-700: #cccccc;
|
||||||
--control-bg: #008AFC; /* Light Blue */
|
--control-bg: #008AFC; /* Light Blue */
|
||||||
|
--icon-stroke: #008AFC;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,11 +35,25 @@ h2 {font-size:3rem}
|
|||||||
/* Button */
|
/* Button */
|
||||||
button {background:#008AFC}
|
button {background:#008AFC}
|
||||||
|
|
||||||
|
/* Form Elements */
|
||||||
|
input.form-control {
|
||||||
|
background-color: var(--fg-color)!important;
|
||||||
|
border-bottom: 1px solid gray;
|
||||||
|
border-radius:0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------- FRAPPE ELEMENTS ------- */
|
/* ------- FRAPPE ELEMENTS ------- */
|
||||||
/* Text */
|
/* Text */
|
||||||
body .text-muted {color:#cccccc}
|
body .text-muted {color:#cccccc!important}
|
||||||
|
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
.web-form-header,form.web-form {border:none};
|
||||||
|
.form-group .ql-container.ql-snow {border:none;background:var(--fg-color)}
|
||||||
|
.form-group .ql-container.ql-snow svg path {stroke:#cccccc}
|
||||||
|
/* Account Info Box */
|
||||||
|
@media only screen and (max-width:767px){
|
||||||
|
.row.account-info {padding:15px}
|
||||||
|
}
|
||||||
|
|
||||||
/* Web Sidebar */
|
/* Web Sidebar */
|
||||||
.web-sidebar {margin-top:60px}
|
.web-sidebar {margin-top:60px}
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"creation": "2025-06-18 20:11:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
|
"filters_json": "[[\"Opportunity\",\"expected_closing\",\"Timespan\",\"this month\",false],[\"Opportunity\",\"status\",\"not in\",[\"Closed\",\"Lost\"],false]]",
|
||||||
|
"function": "Count",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Opportunities Closing This Month",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Opportunities Closing This Month",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Document Type"
|
||||||
|
}
|
||||||
Binary file not shown.
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"creation": "2025-06-18 20:11:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"method": "rangeldigital.rangel_digital.number_card.overdue_opportunities.overdue_opportunities.get_number_card_data",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Overdue Opportunities",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Overdue Opportunities",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Custom"
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_number_card_data():
|
||||||
|
# Get today's date in YYYY-MM-DD format
|
||||||
|
today = datetime.today().strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
# Fetch count of open opportunities closing before today
|
||||||
|
count = frappe.db.count(
|
||||||
|
"Opportunity",
|
||||||
|
filters={
|
||||||
|
"expected_closing": ["<", today],
|
||||||
|
"status": ["not in", ["Closed", "Lost"]]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"value": count,
|
||||||
|
"fieldtype": "Int",
|
||||||
|
"label": _("Overdue Opportunities")
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"aggregate_function_based_on": "base_opportunity_amount",
|
||||||
|
"creation": "2020-07-20 20:17:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
|
"filters_json": "[[\"Opportunity\",\"status\",\"not in\",[\"Closed\",\"Lost\"],false]]",
|
||||||
|
"function": "Sum",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Pipeline Total",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Pipeline Total",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Document Type"
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"aggregate_function_based_on": "base_opportunity_amount",
|
||||||
|
"creation": "2020-07-20 20:17:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
|
"filters_json": "[[\"Opportunity\",\"expected_closing\",\"Timespan\",\"this month\",false],[\"Opportunity\",\"status\",\"not in\",[\"Closed\",\"Lost\"],false]]",
|
||||||
|
"function": "Sum",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Pipeline Total (This Month)",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Pipeline Total (This Month)",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Document Type"
|
||||||
|
}
|
||||||
Binary file not shown.
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"creation": "2020-07-20 20:17:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
|
"filters_json": "[[\"Opportunity\",\"status\",\"not in\",[\"Closed\",\"Lost\"],false]]",
|
||||||
|
"function": "Sum",
|
||||||
|
"method": "rangeldigital.rangel_digital.number_card.pipeline_value.pipeline_value.get_number_card_data",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Pipeline Value",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Pipeline Value",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Custom"
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from datetime import datetime
|
||||||
|
from frappe.utils import get_first_day, get_last_day
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_number_card_data():
|
||||||
|
# Get the first and last day of the current month
|
||||||
|
today = datetime.today()
|
||||||
|
first_day = get_first_day(today).strftime('%Y-%m-%d')
|
||||||
|
last_day = get_last_day(today).strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
# Fetch open opportunities closing this month
|
||||||
|
opportunities = frappe.db.get_all(
|
||||||
|
"Opportunity",
|
||||||
|
filters={
|
||||||
|
"status": ["not in", ["Closed", "Lost"]]
|
||||||
|
},
|
||||||
|
fields=["name", "sales_stage", "base_opportunity_amount"]
|
||||||
|
)
|
||||||
|
|
||||||
|
total_theoretical_value = 0
|
||||||
|
|
||||||
|
for opp in opportunities:
|
||||||
|
probability = 0
|
||||||
|
if opp.sales_stage:
|
||||||
|
probability = frappe.db.get_value("Sales Stage", opp.sales_stage, "opportunity_probability") or 0
|
||||||
|
|
||||||
|
theoretical_value = (opp.base_opportunity_amount or 0) * (probability / 100)
|
||||||
|
total_theoretical_value += theoretical_value
|
||||||
|
|
||||||
|
return {
|
||||||
|
"value": round(total_theoretical_value, 2),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": _("Theoretical Value (This Month)")
|
||||||
|
}
|
||||||
Binary file not shown.
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"creation": "2020-07-20 20:17:15.922486",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Number Card",
|
||||||
|
"document_type": "Opportunity",
|
||||||
|
"dynamic_filters_json": "[[\"Opportunity\",\"company\",\"=\",\"frappe.defaults.get_user_default(\\\"Company\\\")\"]]",
|
||||||
|
"filters_json": "[[\"Opportunity\",\"status\",\"not in\",[\"Closed\",\"Lost\"],false]]",
|
||||||
|
"function": "Sum",
|
||||||
|
"method": "rangeldigital.rangel_digital.number_card.pipeline_value_(this_month).pipeline_value_(this_month).get_number_card_data",
|
||||||
|
"idx": 0,
|
||||||
|
"is_public": 1,
|
||||||
|
"is_standard": 1,
|
||||||
|
"label": "Pipeline Value (This Month)",
|
||||||
|
"modified": "2025-06-19 12:15:53.088837",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "CRM",
|
||||||
|
"name": "Pipeline Value (This Month)",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"show_percentage_stats": 0,
|
||||||
|
"stats_time_interval": "",
|
||||||
|
"type": "Custom"
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from datetime import datetime
|
||||||
|
from frappe.utils import get_first_day, get_last_day
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_number_card_data():
|
||||||
|
# Get the first and last day of the current month
|
||||||
|
today = datetime.today()
|
||||||
|
first_day = get_first_day(today).strftime('%Y-%m-%d')
|
||||||
|
last_day = get_last_day(today).strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
# Fetch open opportunities closing this month
|
||||||
|
opportunities = frappe.db.get_all(
|
||||||
|
"Opportunity",
|
||||||
|
filters={
|
||||||
|
"expected_closing": ["between", [first_day, last_day]],
|
||||||
|
"status": ["not in", ["Closed", "Lost"]]
|
||||||
|
},
|
||||||
|
fields=["name", "sales_stage", "base_opportunity_amount"]
|
||||||
|
)
|
||||||
|
|
||||||
|
total_theoretical_value = 0
|
||||||
|
|
||||||
|
for opp in opportunities:
|
||||||
|
probability = 0
|
||||||
|
if opp.sales_stage:
|
||||||
|
probability = frappe.db.get_value("Sales Stage", opp.sales_stage, "opportunity_probability") or 0
|
||||||
|
|
||||||
|
theoretical_value = (opp.base_opportunity_amount or 0) * (probability / 100)
|
||||||
|
total_theoretical_value += theoretical_value
|
||||||
|
|
||||||
|
return {
|
||||||
|
"value": round(total_theoretical_value, 2),
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"label": _("Theoretical Value (This Month)")
|
||||||
|
}
|
||||||
Binary file not shown.
Reference in New Issue
Block a user