Add ERPNext Plugin documentation for MoneyServer

Added ERPNext Plugin documentation for C# MoneyServer integration, including plugin structure, Doctype definitions, API client implementation, hooks, dashboard, reports, and installation instructions.
This commit is contained in:
ManfredAabye
2025-10-12 16:08:20 +02:00
committed by GitHub
parent 6b4a9c4008
commit 40b7347e7d
+431
View File
@@ -0,0 +1,431 @@
# ERPNext Plugin für C# MoneyServer Integration
## Plugin-Struktur
```
opensim_currency/
├── __init__.py
├── hooks.py
├── patches/
│ └── __init__.py
├── config/
│ ├── docs.py
│ └── __init__.py
├── custom/
│ ├── __init__.py
│ └── opensim_currency.py
├── public/
│ └── js/
│ └── opensim_dashboard.js
├── templates/
│ └── opensim_dashboard.html
└── www/
└── opensim_api.py
```
## 1. Doctype-Definitionen
### OpenSim Currency Server
```python
# opensim_currency/opensim_currency/doctype/opensim_currency_server/opensim_currency_server.py
import frappe
from frappe.model.document import Document
import requests
import json
class OpenSimCurrencyServer(Document):
def validate(self):
self.validate_server_connection()
def validate_server_connection(self):
try:
response = requests.get(f"{self.base_url}/api/health", timeout=10)
if response.status_code != 200:
frappe.throw("Cannot connect to MoneyServer")
except Exception as e:
frappe.throw(f"Connection failed: {str(e)}")
```
### OpenSim Account
```python
# opensim_currency/opensim_currency/doctype/opensim_account/opensim_account.py
import frappe
from frappe.model.document import Document
class OpenSimAccount(Document):
def before_save(self):
if not self.account_uuid:
self.create_remote_account()
def create_remote_account(self):
server = frappe.get_doc("OpenSim Currency Server", self.currency_server)
api = OpenSimCurrencyAPI(server)
result = api.create_account({
"name": self.account_name,
"owner": self.customer,
"currency_type": self.currency_type
})
self.account_uuid = result.get("account_uuid")
self.balance = result.get("balance", 0)
```
### OpenSim Transaction
```python
# opensim_currency/opensim_currency/doctype/opensim_transaction/opensim_transaction.py
import frappe
from frappe.model.document import Document
class OpenSimTransaction(Document):
def on_submit(self):
self.execute_remote_transaction()
def execute_remote_transaction(self):
server = frappe.get_doc("OpenSim Currency Server", self.currency_server)
api = OpenSimCurrencyAPI(server)
if self.transaction_type == "Transfer":
result = api.transfer_funds(
self.from_account_uuid,
self.to_account_uuid,
self.amount,
self.description
)
elif self.transaction_type == "Payment":
result = api.make_payment(
self.from_account_uuid,
self.amount,
self.description
)
self.remote_transaction_id = result.get("transaction_id")
self.status = "Completed"
self.save()
```
## 2. API-Client Implementation
```python
# opensim_currency/opensim_currency/api.py
import requests
import frappe
from frappe import _
import json
class OpenSimCurrencyAPI:
def __init__(self, server_doc):
self.base_url = server_doc.base_url
self.api_key = server_doc.get_password("api_key")
self.headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}"
}
def create_account(self, account_data):
endpoint = f"{self.base_url}/api/account/create"
response = requests.post(endpoint, json=account_data, headers=self.headers)
return self._handle_response(response)
def transfer_funds(self, from_uuid, to_uuid, amount, description=""):
endpoint = f"{self.base_url}/api/transaction/transfer"
data = {
"fromAccount": from_uuid,
"toAccount": to_uuid,
"amount": amount,
"description": description
}
response = requests.post(endpoint, json=data, headers=self.headers)
return self._handle_response(response)
def get_balance(self, account_uuid):
endpoint = f"{self.base_url}/api/account/{account_uuid}/balance"
response = requests.get(endpoint, headers=self.headers)
return self._handle_response(response)
def get_transaction_history(self, account_uuid, limit=100):
endpoint = f"{self.base_url}/api/account/{account_uuid}/transactions"
params = {"limit": limit}
response = requests.get(endpoint, headers=self.headers, params=params)
return self._handle_response(response)
def _handle_response(self, response):
if response.status_code == 200:
return response.json()
else:
frappe.throw(f"API Error: {response.status_code} - {response.text}")
```
## 3. Doctype JSON Definitions
### opensim_currency_server.json
```json
{
"doctype": "DocType",
"module": "OpenSim Currency",
"name": "OpenSim Currency Server",
"is_single": 0,
"istable": 0,
"fields": [
{
"fieldname": "server_name",
"label": "Server Name",
"fieldtype": "Data",
"reqd": 1
},
{
"fieldname": "base_url",
"label": "Base URL",
"fieldtype": "Data",
"reqd": 1
},
{
"fieldname": "api_key",
"label": "API Key",
"fieldtype": "Password",
"reqd": 1
},
{
"fieldname": "currency_code",
"label": "Currency Code",
"fieldtype": "Data",
"default": "OSC"
},
{
"fieldname": "exchange_rate",
"label": "Exchange Rate",
"fieldtype": "Float",
"default": 1.0
},
{
"fieldname": "is_active",
"label": "Is Active",
"fieldtype": "Check",
"default": 1
}
],
"links": [
{
"link_doctype": "OpenSim Account",
"link_fieldname": "currency_server"
}
]
}
```
### opensim_account.json
```json
{
"doctype": "DocType",
"module": "OpenSim Currency",
"name": "OpenSim Account",
"fields": [
{
"fieldname": "account_name",
"label": "Account Name",
"fieldtype": "Data",
"reqd": 1
},
{
"fieldname": "account_uuid",
"label": "Account UUID",
"fieldtype": "Data",
"read_only": 1
},
{
"fieldname": "customer",
"label": "Customer",
"fieldtype": "Link",
"options": "Customer",
"reqd": 1
},
{
"fieldname": "currency_server",
"label": "Currency Server",
"fieldtype": "Link",
"options": "OpenSim Currency Server",
"reqd": 1
},
{
"fieldname": "balance",
"label": "Current Balance",
"fieldtype": "Currency",
"read_only": 1
},
{
"fieldname": "currency_type",
"label": "Currency Type",
"fieldtype": "Select",
"options": "OpenSim\nVirtual\nFiat",
"default": "OpenSim"
},
{
"fieldname": "is_active",
"label": "Is Active",
"fieldtype": "Check",
"default": 1
}
],
"links": [
{
"link_doctype": "OpenSim Transaction",
"link_fieldname": "from_account"
},
{
"link_doctype": "OpenSim Transaction",
"link_fieldname": "to_account"
}
]
}
```
## 4. Hooks und Konfiguration
```python
# opensim_currency/hooks.py
from . import __version__ as app_version
app_name = "opensim_currency"
app_title = "OpenSim Currency"
app_publisher = "Your Company"
app_description = "ERPNext integration for C# MoneyServer"
app_icon = "fa fa-money"
app_color = "green"
app_email = "your-email@example.com"
app_license = "MIT"
# Includes in <head>
# ------------------
# include js, css files in header of desk.html
# app_include_css = "/assets/opensim_currency/css/opensim_currency.css"
# app_include_js = "/assets/opensim_currency/js/opensim_currency.js"
# include custom scss in every website theme (without file extension ".scss")
# website_theme_scss = "opensim_currency/public/scss/website"
# include js, css files in header of web template
# web_include_css = "/assets/opensim_currency/css/opensim_currency.css"
# web_include_js = "/assets/opensim_currency/js/opensim_currency.js"
# include custom scss in every website theme (without file extension ".scss")
# website_theme_scss = "opensim_currency/public/scss/website"
# DocType Class
# ---------------
doctype_js = {
"Customer": "public/js/customer.js"
}
doctype_class = {
"OpenSim Currency Server": "opensim_currency.opensim_currency.doctype.opensim_currency_server.opensim_currency_server.OpenSimCurrencyServer",
"OpenSim Account": "opensim_currency.opensim_currency.doctype.opensim_account.opensim_account.OpenSimAccount",
"OpenSim Transaction": "opensim_currency.opensim_currency.doctype.opensim_transaction.opensim_transaction.OpenSimTransaction"
}
# Fixtures
# ------------
fixtures = [
{
"dt": "Custom Field",
"filters": [
["module", "=", "OpenSim Currency"]
]
}
]
# Scheduled Tasks
# ---------------
scheduler_events = {
"cron": {
"0 2 * * *": [
"opensim_currency.opensim_currency.doctype.opensim_account.opensim_account.sync_balances"
]
}
}
# Methods
# ------------
methods = {
"opensim_currency.api.get_balance": "opensim_currency.opensim_currency.api.get_balance"
}
```
## 5. Dashboard und Reports
```python
# opensim_currency/opensim_currency/report/opensim_transaction_report/opensim_transaction_report.py
import frappe
from frappe import _
def execute(filters=None):
columns = get_columns()
data = get_data(filters)
return columns, data
def get_columns():
return [
{"fieldname": "transaction_id", "label": _("Transaction ID"), "fieldtype": "Data", "width": 200},
{"fieldname": "from_account", "label": _("From Account"), "fieldtype": "Link", "options": "OpenSim Account"},
{"fieldname": "to_account", "label": _("To Account"), "fieldtype": "Link", "options": "OpenSim Account"},
{"fieldname": "amount", "label": _("Amount"), "fieldtype": "Currency", "width": 120},
{"fieldname": "status", "label": _("Status"), "fieldtype": "Data", "width": 100},
{"fieldname": "creation", "label": _("Date"), "fieldtype": "Datetime", "width": 150}
]
def get_data(filters):
conditions = []
if filters.get("from_account"):
conditions.append(f"from_account = '{filters['from_account']}'")
if filters.get("to_account"):
conditions.append(f"to_account = '{filters['to_account']}'")
if filters.get("from_date"):
conditions.append(f"creation >= '{filters['from_date']}'")
if filters.get("to_date"):
conditions.append(f"creation <= '{filters['to_date']}'")
where_clause = " AND ".join(conditions)
if where_clause:
where_clause = "WHERE " + where_clause
query = f"""
SELECT
name as transaction_id,
from_account,
to_account,
amount,
status,
creation
FROM `tabOpenSim Transaction`
{where_clause}
ORDER BY creation DESC
LIMIT 1000
"""
return frappe.db.sql(query, as_dict=True)
```
## 6. Installation
1. **Plugin erstellen:**
```bash
cd /path/to/erpnext-bench
bench new-app opensim_currency
```
2. **Doctypes installieren:**
```bash
bench --site your-site.local install-app opensim_currency
```
3. **Plugin aktivieren:**
```bash
bench --site your-site.local migrate
bench restart
```
## 7. Features
-**Multi-Server Unterstützung**
-**Echtzeit-Kontostandsynchronisation**
-**Transaktionsmanagement**
-**Automatische Währungsumrechnung**
-**Berichte und Analytics**
-**Sicherheits- und Validierungsmechanismen**
-**Scheduled Tasks für automatische Synchronisation**