livrare website cu erp si crm
This commit is contained in:
parent
28773e3a72
commit
5c7bf7c295
257 changed files with 31929 additions and 0 deletions
288
erp_crm/scripts/setup/09-phase2-final.py
Normal file
288
erp_crm/scripts/setup/09-phase2-final.py
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
"""
|
||||
ERPNext Phase 2 Final Setup: Buying, API User, Reports
|
||||
"""
|
||||
import frappe
|
||||
import json
|
||||
|
||||
|
||||
def execute():
|
||||
setup_buying()
|
||||
setup_api_user()
|
||||
setup_print_format()
|
||||
frappe.db.commit()
|
||||
print("\n=== Phase 2 Final Setup Complete ===\n")
|
||||
|
||||
|
||||
def setup_buying():
|
||||
"""2.6 - Configure suppliers and expense categories."""
|
||||
print("[1/3] Configuring suppliers and expenses...")
|
||||
|
||||
company_abbr = "TC"
|
||||
|
||||
# Create expense accounts under existing parent
|
||||
expenses_parent = frappe.db.get_value("Account",
|
||||
{"company": "TOP CLOSSERS SRL", "root_type": "Expense", "is_group": 1, "parent_account": ["like", "%Expenses%"]},
|
||||
"name"
|
||||
)
|
||||
if not expenses_parent:
|
||||
expenses_parent = frappe.db.get_value("Account",
|
||||
{"company": "TOP CLOSSERS SRL", "root_type": "Expense", "is_group": 1},
|
||||
"name"
|
||||
)
|
||||
|
||||
if expenses_parent:
|
||||
expense_accounts = [
|
||||
{"account_name": "Hosting si Infrastructura", "account_type": "Expense Account"},
|
||||
{"account_name": "Servicii Software", "account_type": "Expense Account"},
|
||||
{"account_name": "Marketing si Publicitate", "account_type": "Expense Account"},
|
||||
{"account_name": "Servicii Plati (Stripe)", "account_type": "Expense Account"},
|
||||
]
|
||||
for acc in expense_accounts:
|
||||
full_name = f"{acc['account_name']} - {company_abbr}"
|
||||
if not frappe.db.exists("Account", full_name):
|
||||
try:
|
||||
a = frappe.get_doc({
|
||||
"doctype": "Account",
|
||||
"account_name": acc["account_name"],
|
||||
"parent_account": expenses_parent,
|
||||
"account_type": acc["account_type"],
|
||||
"company": "TOP CLOSSERS SRL",
|
||||
})
|
||||
a.insert(ignore_permissions=True)
|
||||
print(f" Created account: {acc['account_name']}")
|
||||
except Exception as e:
|
||||
print(f" Account {acc['account_name']}: {e}")
|
||||
|
||||
# Create suppliers
|
||||
suppliers = [
|
||||
{"supplier_name": "Hetzner Online GmbH", "supplier_group": "Services", "country": "Germany",
|
||||
"supplier_type": "Company"},
|
||||
{"supplier_name": "Stripe Payments Europe", "supplier_group": "Services", "country": "Ireland",
|
||||
"supplier_type": "Company"},
|
||||
{"supplier_name": "Twilio SendGrid", "supplier_group": "Services", "country": "United States",
|
||||
"supplier_type": "Company"},
|
||||
]
|
||||
|
||||
# Ensure supplier group exists
|
||||
if not frappe.db.exists("Supplier Group", "Services"):
|
||||
sg = frappe.get_doc({"doctype": "Supplier Group", "supplier_group_name": "Services"})
|
||||
sg.insert(ignore_permissions=True)
|
||||
|
||||
for s in suppliers:
|
||||
if not frappe.db.exists("Supplier", s["supplier_name"]):
|
||||
doc = frappe.get_doc({"doctype": "Supplier", **s})
|
||||
doc.insert(ignore_permissions=True)
|
||||
print(f" Created supplier: {s['supplier_name']}")
|
||||
|
||||
print(" Buying configured.")
|
||||
|
||||
|
||||
def setup_api_user():
|
||||
"""2.9 - Create dedicated API user for website integration."""
|
||||
print("[2/3] Creating API user and role...")
|
||||
|
||||
# Create custom role
|
||||
if not frappe.db.exists("Role", "Website Integration"):
|
||||
role = frappe.get_doc({
|
||||
"doctype": "Role",
|
||||
"role_name": "Website Integration",
|
||||
"desk_access": 0,
|
||||
"is_custom": 1
|
||||
})
|
||||
role.insert(ignore_permissions=True)
|
||||
print(" Created role: Website Integration")
|
||||
|
||||
# Set permissions for the role
|
||||
doctypes_read_write = [
|
||||
"Customer", "Lead", "Sales Invoice", "Payment Entry", "Subscription",
|
||||
"Subscription Plan", "Website Content", "Service Agreement",
|
||||
"Analysis Report",
|
||||
"Payment Log", "Item", "Address"
|
||||
]
|
||||
doctypes_read_only = [
|
||||
"Company", "Account", "Sales Taxes and Charges Template",
|
||||
"Email Template", "Sales Stage"
|
||||
]
|
||||
|
||||
for dt in doctypes_read_write:
|
||||
try:
|
||||
# Check if DocType exists
|
||||
if not frappe.db.exists("DocType", dt):
|
||||
continue
|
||||
|
||||
existing_name = frappe.db.exists("Custom DocPerm", {"parent": dt, "role": "Website Integration"})
|
||||
values = {
|
||||
"read": 1,
|
||||
"write": 1,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"print": 1,
|
||||
"export": 1,
|
||||
"submit": 1 if dt in ("Sales Invoice", "Payment Entry") else 0,
|
||||
}
|
||||
|
||||
if existing_name:
|
||||
frappe.db.set_value("Custom DocPerm", existing_name, values, update_modified=False)
|
||||
else:
|
||||
perm = frappe.get_doc({
|
||||
"doctype": "Custom DocPerm",
|
||||
"parent": dt,
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "permissions",
|
||||
"role": "Website Integration",
|
||||
**values,
|
||||
})
|
||||
perm.insert(ignore_permissions=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for dt in doctypes_read_only:
|
||||
if not frappe.db.exists("Custom DocPerm", {"parent": dt, "role": "Website Integration"}):
|
||||
try:
|
||||
if frappe.db.exists("DocType", dt):
|
||||
perm = frappe.get_doc({
|
||||
"doctype": "Custom DocPerm",
|
||||
"parent": dt,
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "permissions",
|
||||
"role": "Website Integration",
|
||||
"read": 1,
|
||||
"write": 0,
|
||||
"create": 0,
|
||||
})
|
||||
perm.insert(ignore_permissions=True)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Create API user
|
||||
api_user_email = "website_api@didi.localhost"
|
||||
if not frappe.db.exists("User", api_user_email):
|
||||
user = frappe.get_doc({
|
||||
"doctype": "User",
|
||||
"email": api_user_email,
|
||||
"first_name": "Website",
|
||||
"last_name": "API",
|
||||
"enabled": 1,
|
||||
"user_type": "System User",
|
||||
"roles": [
|
||||
{"role": "Website Integration"},
|
||||
],
|
||||
"new_password": "didi_api_secure_pwd_2026!",
|
||||
"send_welcome_email": 0,
|
||||
})
|
||||
user.insert(ignore_permissions=True)
|
||||
print(f" Created API user: {api_user_email}")
|
||||
|
||||
# Generate API keys
|
||||
api_secret = frappe.generate_hash(length=15)
|
||||
user.reload()
|
||||
user.api_key = frappe.generate_hash(length=15)
|
||||
user.api_secret = api_secret
|
||||
user.save(ignore_permissions=True)
|
||||
|
||||
print(f" API Key: {user.api_key}")
|
||||
print(f" API Secret: {api_secret}")
|
||||
print(f" >>> Save these credentials in website/.env.local <<<")
|
||||
else:
|
||||
user = frappe.get_doc("User", api_user_email)
|
||||
print(f" API user already exists: {api_user_email}")
|
||||
print(f" API Key: {user.api_key}")
|
||||
|
||||
print(" API user configured.")
|
||||
|
||||
|
||||
def setup_print_format():
|
||||
"""2.3.2 - Create custom invoice PDF print format."""
|
||||
print("[3/3] Creating custom invoice print format...")
|
||||
|
||||
if frappe.db.exists("Print Format", "DiDi Invoice"):
|
||||
print(" Already exists, skipping.")
|
||||
return
|
||||
|
||||
html = """
|
||||
<style>
|
||||
.didi-invoice { font-family: Arial, sans-serif; font-size: 12px; color: #333; }
|
||||
.didi-invoice .header { display: flex; justify-content: space-between; margin-bottom: 30px; }
|
||||
.didi-invoice .company-info { text-align: right; }
|
||||
.didi-invoice .invoice-title { font-size: 24px; font-weight: bold; color: #2563eb; margin-bottom: 5px; }
|
||||
.didi-invoice table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||
.didi-invoice th { background: #2563eb; color: white; padding: 8px 12px; text-align: left; }
|
||||
.didi-invoice td { padding: 8px 12px; border-bottom: 1px solid #e5e7eb; }
|
||||
.didi-invoice .totals { text-align: right; margin-top: 20px; }
|
||||
.didi-invoice .totals td { font-weight: bold; }
|
||||
.didi-invoice .footer { margin-top: 40px; padding-top: 20px; border-top: 2px solid #2563eb; font-size: 10px; color: #666; }
|
||||
</style>
|
||||
<div class="didi-invoice">
|
||||
<div class="header">
|
||||
<div>
|
||||
<div class="invoice-title">FACTURA</div>
|
||||
<div><strong>{{ doc.name }}</strong></div>
|
||||
<div>Data: {{ doc.posting_date }}</div>
|
||||
<div>Scadenta: {{ doc.due_date }}</div>
|
||||
</div>
|
||||
<div class="company-info">
|
||||
<div><strong>{{ doc.company }}</strong></div>
|
||||
<div>CUI: {{ frappe.db.get_value("Company", doc.company, "tax_id") }}</div>
|
||||
<div>{{ frappe.db.get_value("Company", doc.company, "address") or "Str. Targovistei 15, Ploiesti" }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 20px;">
|
||||
<strong>Catre:</strong><br>
|
||||
{{ doc.customer_name }}<br>
|
||||
{% if doc.tax_id %}CUI: {{ doc.tax_id }}<br>{% endif %}
|
||||
{{ doc.address_display or "" }}
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nr.</th>
|
||||
<th>Descriere</th>
|
||||
<th>Cant.</th>
|
||||
<th>Pret unitar</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in doc.items %}
|
||||
<tr>
|
||||
<td>{{ loop.index }}</td>
|
||||
<td>{{ item.item_name }}<br><small>{{ item.description or "" }}</small></td>
|
||||
<td>{{ item.qty }}</td>
|
||||
<td>{{ frappe.format(item.rate, {"fieldtype": "Currency", "currency": doc.currency}) }}</td>
|
||||
<td>{{ frappe.format(item.amount, {"fieldtype": "Currency", "currency": doc.currency}) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="totals" style="width: 300px; margin-left: auto;">
|
||||
<tr><td>Subtotal:</td><td>{{ frappe.format(doc.net_total, {"fieldtype": "Currency", "currency": doc.currency}) }}</td></tr>
|
||||
{% for tax in doc.taxes %}
|
||||
<tr><td>{{ tax.description }}:</td><td>{{ frappe.format(tax.tax_amount, {"fieldtype": "Currency", "currency": doc.currency}) }}</td></tr>
|
||||
{% endfor %}
|
||||
<tr style="font-size: 16px;"><td>TOTAL:</td><td>{{ frappe.format(doc.grand_total, {"fieldtype": "Currency", "currency": doc.currency}) }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="footer">
|
||||
<p>Factura generata automat de platforma DiDi. Acest document este valid fara semnatura si stampila conform art. 106 alin. 2 din Legea 227/2015.</p>
|
||||
<p>TOP CLOSSERS SRL | CUI: 36193026 | J2022000345035 | office@clossers.com</p>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
pf = frappe.get_doc({
|
||||
"doctype": "Print Format",
|
||||
"name": "DiDi Invoice",
|
||||
"doc_type": "Sales Invoice",
|
||||
"module": "Didi Custom",
|
||||
"html": html,
|
||||
"print_format_type": "Jinja",
|
||||
"standard": "No",
|
||||
"custom_format": 1,
|
||||
"default_print_language": "ro",
|
||||
})
|
||||
pf.insert(ignore_permissions=True)
|
||||
print(" DiDi Invoice print format created.")
|
||||
Loading…
Add table
Add a link
Reference in a new issue