mirror of
https://github.com/ManfredAabye/opensimcurrencyserver-dotnet-tests.git
synced 2026-08-14 00:47:56 +00:00
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:
+431
@@ -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**
|
||||
Reference in New Issue
Block a user