journal
This commit is contained in:
parent
f52e76374e
commit
042fd7bdcf
4 changed files with 5652 additions and 33 deletions
148
IMIXS_JOURNAL.md
Normal file
148
IMIXS_JOURNAL.md
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
Ich habe 2 SQL Datenbank tabellen mit Umsatzinformationen. Hier Beispieldaten:
|
||||||
|
|
||||||
|
# select \* from journal_accounting;
|
||||||
|
|
||||||
|
id | amount | baseamount | currency | date | exchangerate | reference | type
|
||||||
|
----+------------+------------+----------+------------+--------------+---------------------------------------------------------------------------+------------------
|
||||||
|
27 | 2202.0000 | | EUR | 2025-02-12 | | c530a7aa-52c5-4dcf-be62-fd8946c4050b | Rechnungsausgang
|
||||||
|
28 | 13445.2800 | | EUR | 2025-02-17 | | 5c8fa50b-22fc-4f3f-b302-bb46842bb69e | Rechnungsausgang
|
||||||
|
29 | 3080.0000 | | EUR | 2025-03-06 | | c0e966a5-5a0d-45a7-9413-eeffadd0ea27 | Rechnungsausgang
|
||||||
|
30 | 20730.0000 | | EUR | 2025-02-17 | | c554aacd-b096-4102-aebf-a0cf5023d159 | Rechnungsausgang
|
||||||
|
31 | 370.0000 | | EUR | 2025-03-04 | | 69666156-dd0f-40cc-9f84-543c69e3a534 | Rechnungsausgang
|
||||||
|
32 | 1957.6800 | | EUR | 2025-03-10 | | 8522399b-e953-47e9-b18e-f9cfbc6565b0 | Rechnungsausgang
|
||||||
|
33 | 225.0000 | 220.7400 | USD | 2025-03-07 | 1.019307 | 0528bc9f-b0d9-4079-a89a-af2c978f141d | Rechnungsausgang
|
||||||
|
38 | 2806.9600 | | EUR | 2025-02-18 | | 9d0929a9-1790-4001-bce1-fb376a8032aa | Rechnungsausgang
|
||||||
|
|
||||||
|
# select \* from journal_accounting_attribute;
|
||||||
|
|
||||||
|
id | key | value | journalaccounting_id
|
||||||
|
-----+-------------+------------------------------------------+----------------------
|
||||||
|
1 | space.name | | 1
|
||||||
|
2 | dbtr.name | SHANGHAI AS. DEV. PROSP. INT LOG. Co.Ltd | 1
|
||||||
|
3 | dbtr.number | D18516 | 1
|
||||||
|
4 | dbtr.name | SHANGHAI AS. DEV. PROSP. INT LOG. Co.Ltd | 2
|
||||||
|
5 | space.name | | 2
|
||||||
|
6 | dbtr.number | D18516 | 2
|
||||||
|
7 | dbtr.number | D16571 | 3
|
||||||
|
8 | space.name | | 3
|
||||||
|
9 | dbtr.name | Bixwood AB | 3
|
||||||
|
|
||||||
|
Wie würdest Du ein Select Statement erstellen, das alle Umsätze pro Firma pro Währung nach Datum aufschlüsselt?
|
||||||
|
|
||||||
|
# Umsätze Nach Firma
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
ja.date,
|
||||||
|
ja.currency,
|
||||||
|
jaa.value AS "Firma",
|
||||||
|
COALESCE(jab.value, '') AS "Firmennummer",
|
||||||
|
SUM(ja.amount) AS "Gesamtbetrag"
|
||||||
|
FROM
|
||||||
|
journal_accounting ja
|
||||||
|
LEFT JOIN
|
||||||
|
journal_accounting_attribute jaa ON ja.id = jaa.journalaccounting_id AND jaa.key = 'dbtr.name'
|
||||||
|
LEFT JOIN
|
||||||
|
journal_accounting_attribute jab ON ja.id = jab.journalaccounting_id AND jab.key = 'dbtr.number'
|
||||||
|
WHERE
|
||||||
|
jaa.key IN ('dbtr.name', 'dbtr.number')
|
||||||
|
GROUP BY
|
||||||
|
ja.date,
|
||||||
|
ja.currency,
|
||||||
|
jaa.value,
|
||||||
|
jab.value
|
||||||
|
ORDER BY
|
||||||
|
ja.date,
|
||||||
|
ja.currency,
|
||||||
|
jaa.value;
|
||||||
|
|
||||||
|
# Umsätze nach Firma und Monat
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
DATE_TRUNC('month', ja.date) AS "Monat",
|
||||||
|
ja.currency,
|
||||||
|
jaa.value AS "Firma",
|
||||||
|
SUM(ja.amount) AS "Gesamtbetrag"
|
||||||
|
FROM
|
||||||
|
journal_accounting ja
|
||||||
|
LEFT JOIN
|
||||||
|
journal_accounting_attribute jaa ON ja.id = jaa.journalaccounting_id AND jaa.key = 'dbtr.name'
|
||||||
|
WHERE
|
||||||
|
jaa.key IN ('dbtr.name')
|
||||||
|
GROUP BY
|
||||||
|
"Monat",
|
||||||
|
ja.currency,
|
||||||
|
jaa.value
|
||||||
|
ORDER BY
|
||||||
|
"Monat",
|
||||||
|
ja.currency,
|
||||||
|
jaa.value;
|
||||||
|
|
||||||
|
# Nur nach Monat
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
DATE_TRUNC('month', ja.date) AS "Monat",
|
||||||
|
SUM(ja.amount) AS "Gesamtbetrag"
|
||||||
|
FROM
|
||||||
|
journal_accounting ja
|
||||||
|
WHERE
|
||||||
|
ja.type = 'Rechnungsausgang'
|
||||||
|
GROUP BY
|
||||||
|
"Monat"
|
||||||
|
ORDER BY
|
||||||
|
"Monat";
|
||||||
|
Monat | Gesamtbetrag
|
||||||
|
------------------------+--------------
|
||||||
|
2025-02-01 00:00:00+00 | 39184.2400
|
||||||
|
2025-03-01 00:00:00+00 | 5407.6800
|
||||||
|
|
||||||
|
# Java Impl
|
||||||
|
|
||||||
|
```java
|
||||||
|
import org.imixs.workflow.office.journal.JournalAccounting;
|
||||||
|
import org.imixs.workflow.office.journal.JournalAccounting_;
|
||||||
|
import org.imixs.workflow.office.journal.QJournalAccounting;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
import jakarta.persistence.TypedQuery;
|
||||||
|
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||||
|
import jakarta.persistence.criteria.CriteriaQuery;
|
||||||
|
import jakarta.persistence.criteria.Predicate;
|
||||||
|
import jakarta.persistence.criteria.Root;
|
||||||
|
|
||||||
|
public class JournalAccountingRepository {
|
||||||
|
|
||||||
|
private final EntityManager entityManager;
|
||||||
|
|
||||||
|
public JournalAccountingRepository(EntityManager entityManager) {
|
||||||
|
this.entityManager = entityManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Object[]> findByMonth() {
|
||||||
|
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Object[]> cq = cb.createQuery(Object[].class);
|
||||||
|
Root<JournalAccounting> root = cq.from(JournalAccounting.class);
|
||||||
|
|
||||||
|
// Define the projection
|
||||||
|
cq.multiselect(
|
||||||
|
cb.function("DATE_TRUNC", LocalDate.class, root.get(JournalAccounting_.date), cb.literal("MONTH")),
|
||||||
|
cb.sum(root.get(JournalAccounting_.amount))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Define the where clause
|
||||||
|
Predicate predicate = cb.equal(root.get(JournalAccounting_.type), "Rechnungsausgang");
|
||||||
|
cq.where(predicate);
|
||||||
|
|
||||||
|
// Define the group by clause
|
||||||
|
cq.groupBy(root.get(JournalAccounting_.date).as(LocalDate.class));
|
||||||
|
|
||||||
|
// Order by month
|
||||||
|
cq.orderBy(cb.asc(root.get(JournalAccounting_.date).as(LocalDate.class)));
|
||||||
|
|
||||||
|
TypedQuery<Object[]> query = entityManager.createQuery(cq);
|
||||||
|
return query.getResultList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -5,6 +5,8 @@ import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.imixs.workflow.ItemCollection;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
|
@ -42,6 +44,15 @@ public class JournalAccounting {
|
||||||
@Column()
|
@Column()
|
||||||
private String reference;
|
private String reference;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
private String workflowGroup;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
private String modelVersion;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
private int taskId;
|
||||||
|
|
||||||
@Temporal(TemporalType.DATE)
|
@Temporal(TemporalType.DATE)
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private Date date;
|
private Date date;
|
||||||
|
|
@ -56,10 +67,16 @@ public class JournalAccounting {
|
||||||
public JournalAccounting() {
|
public JournalAccounting() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public JournalAccounting(BigDecimal amount, String type, Date entryDate) {
|
public JournalAccounting(ItemCollection workitem) {
|
||||||
this.amount = amount;
|
updateWorkitem(workitem);
|
||||||
this.type = type;
|
}
|
||||||
this.date = entryDate;
|
|
||||||
|
public void updateWorkitem(ItemCollection workitem) {
|
||||||
|
this.reference = workitem.getUniqueID();
|
||||||
|
this.workflowGroup = workitem.getWorkflowGroup();
|
||||||
|
this.modelVersion = workitem.getModelVersion();
|
||||||
|
this.taskId = workitem.getTaskID();
|
||||||
|
this.type = workitem.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods for attribute management
|
// Helper methods for attribute management
|
||||||
|
|
@ -138,6 +155,30 @@ public class JournalAccounting {
|
||||||
this.reference = businessProcessReference;
|
this.reference = businessProcessReference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getWorkflowGroup() {
|
||||||
|
return workflowGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorkflowGroup(String workflowGroup) {
|
||||||
|
this.workflowGroup = workflowGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getModelVersion() {
|
||||||
|
return modelVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModelVersion(String modelVersion) {
|
||||||
|
this.modelVersion = modelVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTaskId() {
|
||||||
|
return taskId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaskId(int taskId) {
|
||||||
|
this.taskId = taskId;
|
||||||
|
}
|
||||||
|
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return date;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import jakarta.enterprise.event.Observes;
|
||||||
import jakarta.inject.Inject;
|
import jakarta.inject.Inject;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.PersistenceContext;
|
import jakarta.persistence.PersistenceContext;
|
||||||
|
import jakarta.persistence.TypedQuery;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Der BusinessPartnerService stellt Methoden für den Zugriff auf Business
|
* Der BusinessPartnerService stellt Methoden für den Zugriff auf Business
|
||||||
|
|
@ -92,8 +93,7 @@ public class JournalAccountingService {
|
||||||
if (WorkflowEvent.WORKITEM_AFTER_PROCESS == workflowEvent.getEventType()) {
|
if (WorkflowEvent.WORKITEM_AFTER_PROCESS == workflowEvent.getEventType()) {
|
||||||
|
|
||||||
// create / update?
|
// create / update?
|
||||||
if (workitem.getModelVersion().startsWith("rechnungsausgang-")
|
if (workitem.getModelVersion().startsWith("rechnungsausgang-")) {
|
||||||
|| workitem.getModelVersion().startsWith("rechnungsausgang-")) {
|
|
||||||
processInvoice(workitem);
|
processInvoice(workitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,23 +106,22 @@ public class JournalAccountingService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process an invoice workitem - creates or updates accounting entry
|
* Process an invoice workitem - creates or updates accounting entry
|
||||||
|
*
|
||||||
|
* The reference is the uniqueId of the invoice.
|
||||||
*/
|
*/
|
||||||
private void processInvoice(ItemCollection workitem) {
|
private void processInvoice(ItemCollection workitem) {
|
||||||
String businessProcessRef = workitem.getUniqueID();
|
|
||||||
|
|
||||||
// Try to find existing entry
|
// Try to find existing entry
|
||||||
JournalAccounting accounting = findAccountingEntryByBusinessRef(businessProcessRef,
|
JournalAccounting accounting = findAccountingEntryByInvoice(workitem.getUniqueID());
|
||||||
workitem.getWorkflowGroup());
|
|
||||||
|
|
||||||
// Create new or update existing
|
// Create new or update existing
|
||||||
if (accounting == null) {
|
if (accounting == null) {
|
||||||
accounting = new JournalAccounting();
|
accounting = new JournalAccounting(workitem);
|
||||||
accounting.setReference(businessProcessRef);
|
} else {
|
||||||
accounting.setType(workitem.getWorkflowGroup());
|
accounting.updateWorkitem(workitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update/set values
|
// Update/set values
|
||||||
|
|
||||||
accounting.setDate(workitem.getItemValueDate("invoice.date"));
|
accounting.setDate(workitem.getItemValueDate("invoice.date"));
|
||||||
|
|
||||||
BigDecimal amount = BigDecimal.valueOf(workitem.getItemValueDouble("invoice.total"))
|
BigDecimal amount = BigDecimal.valueOf(workitem.getItemValueDouble("invoice.total"))
|
||||||
|
|
@ -169,31 +168,29 @@ public class JournalAccountingService {
|
||||||
List<ItemCollection> paymentDetails = InvoiceUtil.explodeChildList(workitem, "payment.details");
|
List<ItemCollection> paymentDetails = InvoiceUtil.explodeChildList(workitem, "payment.details");
|
||||||
|
|
||||||
for (ItemCollection payment : paymentDetails) {
|
for (ItemCollection payment : paymentDetails) {
|
||||||
|
// load the invoice document
|
||||||
String invoiceRef = payment.getItemValueString("$uniqueid");
|
String invoiceRef = payment.getItemValueString("$uniqueid");
|
||||||
BigDecimal amount = BigDecimal.valueOf(payment.getItemValueDouble("payment.amount"))
|
ItemCollection invoice = documentService.load(invoiceRef);
|
||||||
|
|
||||||
|
BigDecimal amount = BigDecimal.valueOf(-payment.getItemValueDouble("payment.amount"))
|
||||||
.setScale(2, RoundingMode.HALF_UP);
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
// Find existing payment entry or create new one
|
// Find existing payment entry or create new one
|
||||||
String paymentBusinessRef = workitem.getUniqueID() + "-" + invoiceRef;
|
JournalAccounting accounting = findAccountingEntryByPaymentReference(invoice.getUniqueID(),
|
||||||
JournalAccounting accounting = findAccountingEntryByBusinessRef(paymentBusinessRef,
|
workitem.getUniqueID());
|
||||||
workitem.getWorkflowGroup());
|
|
||||||
|
|
||||||
if (accounting == null) {
|
if (accounting == null) {
|
||||||
accounting = new JournalAccounting();
|
accounting = new JournalAccounting(workitem);
|
||||||
accounting.setReference(paymentBusinessRef);
|
} else {
|
||||||
accounting.setType(workitem.getWorkflowGroup());
|
// update
|
||||||
|
accounting.updateWorkitem(workitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set reference to invoice
|
||||||
|
accounting.setReference(invoice.getUniqueID());
|
||||||
|
|
||||||
// Update payment values
|
// Update payment values
|
||||||
accounting.setDate(workitem.getItemValueDate("payment.date"));
|
accounting.setDate(workitem.getItemValueDate("payment.date"));
|
||||||
|
|
||||||
// Find the related invoice entry
|
|
||||||
JournalAccounting invoiceEntry = findAccountingEntryByBusinessRef(invoiceRef, "Rechnungsausgang");
|
|
||||||
if (invoiceEntry != null) {
|
|
||||||
accounting.addAttribute("Invoice-Ref", invoiceEntry.getReference());
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemCollection invoice = documentService.load(invoiceRef);
|
|
||||||
accounting.setCurrency(invoice.getItemValueString("invoice.currency"));
|
accounting.setCurrency(invoice.getItemValueString("invoice.currency"));
|
||||||
accounting.setAmount(amount);
|
accounting.setAmount(amount);
|
||||||
// foreign currency
|
// foreign currency
|
||||||
|
|
@ -215,6 +212,7 @@ public class JournalAccountingService {
|
||||||
// Update attributes
|
// Update attributes
|
||||||
// Clear and add attributes (or use selective update if needed)
|
// Clear and add attributes (or use selective update if needed)
|
||||||
accounting.getAttributes().clear();
|
accounting.getAttributes().clear();
|
||||||
|
accounting.addAttribute("payment.reference", workitem.getUniqueID());
|
||||||
accounting.addAttribute("dbtr.name", workitem.getItemValueString("dbtr.name"));
|
accounting.addAttribute("dbtr.name", workitem.getItemValueString("dbtr.name"));
|
||||||
accounting.addAttribute("dbtr.number", workitem.getItemValueString("dbtr.number"));
|
accounting.addAttribute("dbtr.number", workitem.getItemValueString("dbtr.number"));
|
||||||
accounting.addAttribute("space.name", workitem.getItemValueString("space.name"));
|
accounting.addAttribute("space.name", workitem.getItemValueString("space.name"));
|
||||||
|
|
@ -231,19 +229,42 @@ public class JournalAccountingService {
|
||||||
/**
|
/**
|
||||||
* Find an accounting entry by its business process reference
|
* Find an accounting entry by its business process reference
|
||||||
*/
|
*/
|
||||||
private JournalAccounting findAccountingEntryByBusinessRef(String businessProcessRef, String type) {
|
private JournalAccounting findAccountingEntryByInvoice(String uniqueId) {
|
||||||
try {
|
try {
|
||||||
return entityManager.createQuery(
|
return entityManager.createQuery(
|
||||||
"SELECT a FROM JournalAccounting a " +
|
"SELECT a FROM JournalAccounting a " +
|
||||||
"WHERE a.reference = :ref " +
|
"WHERE a.reference = :ref ",
|
||||||
"AND a.type = :type",
|
|
||||||
JournalAccounting.class)
|
JournalAccounting.class)
|
||||||
.setParameter("ref", businessProcessRef)
|
.setParameter("ref", uniqueId)
|
||||||
.setParameter("type", type)
|
|
||||||
.getSingleResult();
|
.getSingleResult();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// No result found
|
// No result found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find an invoice accounting entry pointing to a given payment reference
|
||||||
|
*
|
||||||
|
* @param invoiceReference
|
||||||
|
* @param paymentReference
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JournalAccounting findAccountingEntryByPaymentReference(String invoiceReference, String paymentReference) {
|
||||||
|
try {
|
||||||
|
String jpql = "SELECT account FROM JournalAccounting account " +
|
||||||
|
"JOIN account.attributes atr " +
|
||||||
|
"WHERE account.reference = :reference " +
|
||||||
|
"AND atr.key = 'payment.reference' AND atr.value = :paymentReference";
|
||||||
|
|
||||||
|
TypedQuery<JournalAccounting> query = entityManager.createQuery(jpql, JournalAccounting.class);
|
||||||
|
query.setParameter("reference", invoiceReference);
|
||||||
|
query.setParameter("paymentReference", paymentReference);
|
||||||
|
|
||||||
|
return query.getSingleResult();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// No result found
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5409
workflow/pl/rechnungseingang-sachrechnung-pl-1.0.3.bpmn
Normal file
5409
workflow/pl/rechnungseingang-sachrechnung-pl-1.0.3.bpmn
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue