Net/Gross margin calculation completed
authorVásáry Dániel <vasary@elgekko.net>
Mon, 4 Mar 2024 15:26:55 +0000 (16:26 +0100)
committerVásáry Dániel <vasary@elgekko.net>
Mon, 4 Mar 2024 15:26:55 +0000 (16:26 +0100)
sly-crm-app/src/main/resources/logback-dev.xml
sly-crm-db/migrations/scripts/030_add_price_exchange_fields_to_invoice.sql [new file with mode: 0644]
sly-crm-db/migrations/scripts/031_add_price_exchange_fields_to_treasury.sql [new file with mode: 0644]
sly-crm-db/src/main/java/hu/user/lis/db/Invoice.java
sly-crm-db/src/main/java/hu/user/lis/db/Treasury.java
sly-crm-ui/src/main/java/hu/user/lis/ui/data/IncomeMargin.java [deleted file]
sly-crm-ui/src/main/java/hu/user/lis/ui/data/IncomeMarginsDataModel.java
sly-crm-ui/src/main/java/hu/user/lis/ui/data/MoneyAmount.java [new file with mode: 0644]
sly-crm-ui/src/main/java/hu/user/lis/ui/editor/ProjectEditorModel.java
sly-crm-ui/src/main/resources/web/editor/project-editor.zul

index 5773e16b7885245347871ca0e5175b6ca0a4fcfb..b040aa4510c307f54f01badcd1455f4e271af538 100644 (file)
@@ -3,7 +3,7 @@
     <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
         <layout class="ch.qos.logback.classic.PatternLayout">
             <Pattern>
-                %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.}): %msg%n%throwable
+                %black(%d{ISO8601}) %highlight(%-5level) [%blue(%t)] %yellow(%C{1.} \(%line\)): %msg%n%throwable
             </Pattern>
         </layout>
     </appender>
diff --git a/sly-crm-db/migrations/scripts/030_add_price_exchange_fields_to_invoice.sql b/sly-crm-db/migrations/scripts/030_add_price_exchange_fields_to_invoice.sql
new file mode 100644 (file)
index 0000000..b73650b
--- /dev/null
@@ -0,0 +1,16 @@
+-- // add price exchange fields to invoice
+-- Migration SQL that makes the change goes here.
+
+ALTER TABLE invoice
+    ADD COLUMN net_amount_huf DOUBLE
+    ADD COLUMN gross_amount_huf DOUBLE
+    ADD COLUMN exchange_rate DOUBLE;
+
+-- //@UNDO
+-- SQL to undo the change goes here.
+
+ALTER TABLE invoice
+    DROP COLUMN net_amount_huf
+    DROP COLUMN gross_amount_huf
+    DROP COLUMN exchange_rate;
+CALL SYSPROC.ADMIN_CMD('REORG TABLE invoice');
diff --git a/sly-crm-db/migrations/scripts/031_add_price_exchange_fields_to_treasury.sql b/sly-crm-db/migrations/scripts/031_add_price_exchange_fields_to_treasury.sql
new file mode 100644 (file)
index 0000000..75b3b11
--- /dev/null
@@ -0,0 +1,18 @@
+-- // add price exchange fields to treasury
+-- Migration SQL that makes the change goes here.
+
+ALTER TABLE treasury
+    ADD COLUMN buy_amount_huf DOUBLE
+    ADD COLUMN buy_exchange_rate DOUBLE
+    ADD COLUMN sell_amount_huf DOUBLE
+    ADD COLUMN sell_exchange_rate DOUBLE;
+
+-- //@UNDO
+-- SQL to undo the change goes here.
+
+ALTER TABLE treasury
+    DROP COLUMN buy_amount_huf
+    DROP COLUMN buy_exchange_rate
+    DROP COLUMN sell_amount_huf
+    DROP COLUMN sell_exchange_rate;
+CALL SYSPROC.ADMIN_CMD('REORG TABLE treasury');
index 8c5fe92172fcbbffd7f72227c95a4f810d7b3500..8a3f2e24527a8e48ec4f04fe1d0c0f4293c86a15 100644 (file)
@@ -6,7 +6,15 @@ import lombok.NoArgsConstructor;
 import lombok.Setter;
 import lombok.experimental.SuperBuilder;
 
-import javax.persistence.*;
+import javax.persistence.Column;
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorType;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToOne;
 import java.util.Date;
 
 @Getter
@@ -40,6 +48,14 @@ public class Invoice extends IdEntity {
 
     private double vatAmount;
 
+    @Column(name = "net_amount_huf")
+    private Double netAmountHUF;
+
+    @Column(name = "gross_amount_huf")
+    private Double grossAmountHUF;
+
+    private Double exchangeRate;
+
     private Date completionDate;
 
     private Date createDate;
index a0adcd729913d039b4df0599396b310d61a6407b..7756871cada4d77eba711b8cc243afb84a98e2ef 100644 (file)
@@ -7,6 +7,7 @@ import lombok.NoArgsConstructor;
 import lombok.Setter;
 import lombok.experimental.SuperBuilder;
 
+import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.JoinColumn;
 import javax.persistence.ManyToOne;
@@ -29,10 +30,20 @@ public class Treasury extends IdEntity {
 
     private double buyAmount;
 
+    @Column(name = "buy_amount_huf")
+    private Double buyAmountHUF;
+
+    private Double buyExchangeRate;
+
     private Currency buyCurrency;
 
     private double sellAmount;
 
+    @Column(name = "sell_amount_huf")
+    private Double sellAmountHUF;
+
+    private Double sellExchangeRate;
+
     private Currency sellCurrency;
 
     private Date transactionDate;
diff --git a/sly-crm-ui/src/main/java/hu/user/lis/ui/data/IncomeMargin.java b/sly-crm-ui/src/main/java/hu/user/lis/ui/data/IncomeMargin.java
deleted file mode 100644 (file)
index 98b119c..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-package hu.user.lis.ui.data;
-
-import hu.user.lis.db.Currency;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.Setter;
-
-@Getter
-@Setter
-@Builder
-public class
-IncomeMargin {
-    Currency currency;
-    Double amount;
-}
index 78c4c2032276b949805a5545716a281d4db31e22..e58ed2dd05cde2412654e582580772bf13efdf02 100644 (file)
@@ -1,69 +1,76 @@
 package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Currency;
-import hu.user.lis.db.*;
+import hu.user.lis.db.IncomingInvoice;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.OutgoingInvoice;
+import hu.user.lis.db.Project;
+import hu.user.lis.db.ServiceRecord;
+import hu.user.lis.db.Treasury;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
 import org.springframework.context.annotation.Scope;
 import org.springframework.stereotype.Component;
 import org.zkoss.bind.BindUtils;
 
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 
 @Component
 @Log4j2
 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-public class IncomeMarginsDataModel extends ArrayList<IncomeMargin> {
-    public void recalculate(Project project, ServiceRecordsDataModel serviceRecordsDataModel) {
+public class IncomeMarginsDataModel extends ArrayList<MoneyAmount> {
+    public void recalculate(Project project, ServiceRecordsDataModel serviceRecordsDataModel) throws Exception {
         clear();
-        Map<Currency, Double> balances = new HashMap<>();
+        Map<Currency, MoneyAmount> balances = new HashMap<>();
         List<OutgoingInvoice> outgoingInvoices = project.getOutgoingInvoices();
         if (Objects.nonNull(outgoingInvoices)) {
             for (Invoice invoice : outgoingInvoices) {
-                addBalance(balances, invoice.getCurrency(), invoice.getNetAmount());
+                addBalance(balances, MoneyAmount.fromInvoice(invoice));
             }
         }
 
         List<IncomingInvoice> incomingInvoices = project.getIncomingInvoices();
         if (Objects.nonNull(incomingInvoices)) {
             for (Invoice invoice : incomingInvoices) {
-                substractBalance(balances, invoice.getCurrency(), invoice.getNetAmount());
+                subtractBalance(balances, MoneyAmount.fromInvoice(invoice));
             }
         }
 
         List<Treasury> treasuries = project.getTreasuries();
         if (Objects.nonNull(treasuries)) {
             for (Treasury treasury : treasuries) {
-                substractBalance(balances, treasury.getSellCurrency(), treasury.getSellAmount());
-                addBalance(balances, treasury.getBuyCurrency(), treasury.getBuyAmount());
+                subtractBalance(balances, MoneyAmount.fromTreasuryBuy(treasury));
+                addBalance(balances, MoneyAmount.fromTreasurySell(treasury));
             }
         }
 
-
         for (int i = 0; i < serviceRecordsDataModel.getSize(); i++) {
             ServiceRecord serviceRecord = serviceRecordsDataModel.getElementAt(i);
-            substractBalance(balances, Currency.HUF, serviceRecord.getCost());
+            subtractBalance(balances, MoneyAmount.fromServiceRecord(serviceRecord));
         }
 
-        balances.entrySet().stream()
-                .map(s -> IncomeMargin.builder().currency(s.getKey()).amount(s.getValue()).build())
-                .sorted(Comparator.comparing(IncomeMargin::getCurrency))
-                .collect(Collectors.toCollection(() -> this));
-
+        balances.keySet().stream().sorted().forEach(currency -> this.add(balances.get(currency)));
         BindUtils.postNotifyChange(this, "*");
     }
 
 
-    private void addBalance(Map<Currency, Double> balances, Currency currency, double amount) {
-        double currentBalance = balances.containsKey(currency) ? balances.get(currency) : 0;
-        currentBalance += amount;
-        balances.put(currency, currentBalance);
+    private void addBalance(Map<Currency, MoneyAmount> balances, MoneyAmount moneyAmount) throws Exception {
+        MoneyAmount currentBalance = balances.containsKey(moneyAmount.getCurrency()) ?
+                balances.get(moneyAmount.getCurrency()) :
+                MoneyAmount.builder().currency(moneyAmount.getCurrency()).build();
+        currentBalance.add(moneyAmount);
+        balances.put(moneyAmount.getCurrency(), currentBalance);
     }
 
-    private void substractBalance(Map<Currency, Double> balances, Currency currency, double amount) {
-        double currentBalance = balances.containsKey(currency) ? balances.get(currency) : 0;
-        currentBalance -= amount;
-        balances.put(currency, currentBalance);
+    private void subtractBalance(Map<Currency, MoneyAmount> balances, MoneyAmount moneyAmount) throws Exception {
+        MoneyAmount currentBalance = balances.containsKey(moneyAmount.getCurrency()) ?
+                balances.get(moneyAmount.getCurrency()) :
+                MoneyAmount.builder().currency(moneyAmount.getCurrency()).build();
+        currentBalance.subtract(moneyAmount);
+        balances.put(moneyAmount.getCurrency(), currentBalance);
     }
 }
diff --git a/sly-crm-ui/src/main/java/hu/user/lis/ui/data/MoneyAmount.java b/sly-crm-ui/src/main/java/hu/user/lis/ui/data/MoneyAmount.java
new file mode 100644 (file)
index 0000000..e1ca66a
--- /dev/null
@@ -0,0 +1,115 @@
+package hu.user.lis.ui.data;
+
+import hu.user.lis.db.Currency;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.ServiceRecord;
+import hu.user.lis.db.Treasury;
+import hu.user.lis.db.Vat;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Objects;
+
+@Getter
+@Setter
+@Builder
+public class MoneyAmount {
+    Currency currency;
+
+    @Builder.Default
+    Double netAmount = (double) 0;
+    @Builder.Default
+    Double grossAmount = (double) 0;
+    @Builder.Default
+    Double netAmountHUF = (double) 0;
+    @Builder.Default
+    Double grossAmountHUF = (double) 0;
+    @Builder.Default
+    Double exchangeRate = (double) 0;
+
+    public void add(MoneyAmount money) throws Exception {
+        if (!money.getCurrency().equals(currency)) {
+            throw new Exception("Can't add different currencies");
+        }
+        if (Objects.nonNull(money.getNetAmount())) {
+            netAmount += money.getNetAmount();
+        }
+        if (Objects.nonNull(money.getGrossAmount())) {
+            grossAmount += money.getGrossAmount();
+        }
+        if (Objects.nonNull(money.getNetAmountHUF())) {
+            netAmountHUF += money.getNetAmountHUF();
+        }
+        if (Objects.nonNull(money.getGrossAmountHUF())) {
+            grossAmountHUF += money.getGrossAmountHUF();
+        }
+    }
+
+    public void subtract(MoneyAmount money) throws Exception {
+        if (!money.getCurrency().equals(currency)) {
+            throw new Exception("Can't subtract different currencies");
+        }
+        if (Objects.nonNull(money.getNetAmount())) {
+            netAmount -= money.getNetAmount();
+        }
+        if (Objects.nonNull(money.getGrossAmount())) {
+            grossAmount -= money.getGrossAmount();
+        }
+        if (Objects.nonNull(money.getNetAmountHUF())) {
+            netAmountHUF -= money.getNetAmountHUF();
+        }
+        if (Objects.nonNull(money.getGrossAmountHUF())) {
+            grossAmountHUF -= money.getGrossAmountHUF();
+        }
+    }
+
+    public static MoneyAmount fromInvoice(Invoice invoice) {
+        return MoneyAmount.builder()
+                .currency(invoice.getCurrency())
+                .netAmount(invoice.getNetAmount())
+                .grossAmount(invoice.getGrossAmount())
+                .netAmountHUF(invoice.getNetAmountHUF())
+                .grossAmountHUF(invoice.getGrossAmountHUF())
+                .exchangeRate(invoice.getExchangeRate())
+                .build();
+    }
+
+    public static MoneyAmount fromTreasuryBuy(Treasury treasury) {
+        return MoneyAmount.builder()
+                .currency(treasury.getBuyCurrency())
+                .netAmount(treasury.getBuyAmount())
+                .grossAmount(treasury.getBuyAmount())
+                .netAmountHUF(treasury.getBuyAmountHUF())
+                .build();
+
+    }
+
+    public static MoneyAmount fromTreasurySell(Treasury treasury) {
+        return MoneyAmount.builder()
+                .currency(treasury.getSellCurrency())
+                .netAmount(treasury.getSellAmount())
+                .grossAmount(treasury.getSellAmount())
+                .netAmountHUF(treasury.getSellAmountHUF())
+                .build();
+    }
+
+    public static MoneyAmount fromServiceRecord(ServiceRecord serviceRecord) {
+        Vat monthlyVat = serviceRecord.getAssociate().getMonthlyVat();
+        float vatPercent = monthlyVat.getVal();
+        double grossAmount = serviceRecord.getCost();
+        if (monthlyVat != Vat.PERCENT_0) {
+            grossAmount += vatPercent * serviceRecord.getCost() / 100;
+        }
+
+        return MoneyAmount.builder()
+                .currency(Currency.HUF)
+                .netAmount(serviceRecord.getCost())
+                .grossAmount(grossAmount)
+                .netAmountHUF(serviceRecord.getCost())
+                .grossAmountHUF(grossAmount)
+                .exchangeRate((double) 1)
+                .build();
+
+    }
+}
index 9b2de095dd64cab94a4a5e9c1000fee26c1492aa..4a071afe99bec535c495b63807688cfd8e22b43b 100644 (file)
@@ -1,13 +1,27 @@
 package hu.user.lis.ui.editor;
 
 import com.google.common.collect.ImmutableMap;
-import hu.user.lis.db.*;
+import hu.user.lis.db.Associate;
+import hu.user.lis.db.IncomingInvoice;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.OutgoingInvoice;
+import hu.user.lis.db.Partner;
+import hu.user.lis.db.Project;
+import hu.user.lis.db.ProjectAssociate;
+import hu.user.lis.db.Treasury;
 import hu.user.lis.db.repository.InvoiceRepository;
 import hu.user.lis.service.data.EntityDocumentAction;
 import hu.user.lis.service.data.EntityDocumentService;
 import hu.user.lis.ui.Constants;
 import hu.user.lis.ui.converter.ProjectStatusConverter;
-import hu.user.lis.ui.data.*;
+import hu.user.lis.ui.data.AssociatesDataModel;
+import hu.user.lis.ui.data.IncomeMarginsDataModel;
+import hu.user.lis.ui.data.InvoiceDataModel;
+import hu.user.lis.ui.data.ProjectAssociatesDataModel;
+import hu.user.lis.ui.data.ProjectStatusDataModel;
+import hu.user.lis.ui.data.ProjectsDataModel;
+import hu.user.lis.ui.data.ServiceRecordsDataModel;
+import hu.user.lis.ui.data.TreasuryDataModel;
 import hu.user.lis.ui.editor.common.Editors;
 import hu.user.lis.ui.editor.common.EntityEditorModel;
 import hu.user.lis.ui.editor.validator.FormValidator;
@@ -19,7 +33,11 @@ import lombok.extern.log4j.Log4j2;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.transaction.annotation.Transactional;
 import org.zkoss.bind.BindUtils;
-import org.zkoss.bind.annotation.*;
+import org.zkoss.bind.annotation.BindingParam;
+import org.zkoss.bind.annotation.Command;
+import org.zkoss.bind.annotation.Destroy;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.bind.annotation.NotifyChange;
 import org.zkoss.zk.ui.Component;
 import org.zkoss.zk.ui.event.Event;
 import org.zkoss.zk.ui.select.annotation.WireVariable;
@@ -115,7 +133,7 @@ public class ProjectEditorModel extends EntityEditorModel<Project> {
         initAssociates();
         serviceRecordsDataModel.search(getFormDocument(), true);
         getEntitySelectorRouter().configureSelector(Partner.class, getFormDocument(), "partner");
-        incomeMarginsDataModel.recalculate(getFormDocument(), serviceRecordsDataModel);
+        safeRecalcMargin();
     }
 
 
@@ -415,7 +433,16 @@ public class ProjectEditorModel extends EntityEditorModel<Project> {
     @Override
     public void validate() {
         super.validate();
-        incomeMarginsDataModel.recalculate(getFormDocument(), serviceRecordsDataModel);
+        safeRecalcMargin();
     }
 
+    private void safeRecalcMargin() {
+        try {
+            incomeMarginsDataModel.recalculate(getFormDocument(), serviceRecordsDataModel);
+        } catch (Exception e) {
+            log.error(e.getMessage());
+        }
+    }
+
+
 }
index 9fa18b91909da7c5e6e708e2931b89375ace8dc6..b795876426b9f37c8eaf87ab44d5e5c3088885ea 100644 (file)
                                 <tabpanels>
                                     <tabpanel>
                                         <vlayout vflex="true">
+                                            <window title="Árrés (HUF)">
+                                                <hbox>
+                                                    <panel title="Bevétel">
+                                                        <panelchildren>
+                                                            <label value="@load(vm.formDocument.supplyPrice)"/>
+                                                        </panelchildren>
+                                                    </panel>
+                                                    <panel title="Kiadás">
+                                                        <panelchildren>
+                                                            <label value="@load(vm.formDocument.sellingPrice)"/>
+                                                        </panelchildren>
+                                                    </panel>
+                                                    <panel title="Árrés">
+                                                        <panelchildren>
+                                                            <label value="@load(vm.formDocument.margin)"/>
+                                                        </panelchildren>
+                                                    </panel>
+                                                </hbox>
+                                            </window>
                                             <grid sclass="no-hover-grid" oddRowSclass="none"
                                                   model="@load(vm.incomeMarginsDataModel)"
                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"
                                                   emptyMessage="Nem kalkulálható!">
                                                 <columns visible="false">
                                                     <column width="50px" label="Pénznem"/>
-                                                    <column width="100%" label="Összeg"/>
+                                                    <column width="100%" label="Nettó összeg"/>
+                                                    <column width="100%" label="Bruttó összeg"/>
                                                 </columns>
                                                 <rows>
                                                     <template name="model">
                                                         <row>
                                                             <label value="@load(each.currency)"/>
-                                                            <label value="@load(each.amount) @converter('hu.user.lis.ui.converter.DoubleToStringConverter')"/>
+                                                            <label value="@load(each.netAmount) @converter('hu.user.lis.ui.converter.DoubleToStringConverter')"/>
+                                                            <label value="@load(each.grossAmount) @converter('hu.user.lis.ui.converter.DoubleToStringConverter')"/>
                                                         </row>
                                                     </template>
                                                 </rows>
                                             </grid>
-
                                         </vlayout>
                                     </tabpanel>
                                 </tabpanels>