Subversion Repositories SmartDukaan

Rev

Blame | Last modification | View Log | RSS feed

package com.spice.profitmandi.service.inventory;

import com.spice.profitmandi.common.util.FormattingUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;

/**
 * What a return is worth when Finance settles it as B2C after NIC refused it for a cancelled or
 * invalid buyer GSTIN: every returned line at its value net of GST, in whole rupees.
 * <p>
 * Standard accounting rounding - each line's net value is rounded HALF_UP to the rupee, and the
 * refund is the sum of those lines, so the line table and the total always agree. The gross is
 * rounded the same way, and the GST retained is simply gross minus refund. The same object feeds
 * the Finance popup, both notification mails and the server-side check of the amount Finance types
 * in, so the three can never disagree.
 */
public class B2cRefundQuote {

        public static class Line {
                private final int refId;
                private final String description;
                private final long quantity;
                private final BigDecimal grossAmount;
                private final BigDecimal gstRate;
                private final long netAmount;

                Line(int refId, String description, long quantity, BigDecimal grossAmount, BigDecimal gstRate) {
                        this.refId = refId;
                        this.description = description;
                        this.quantity = quantity;
                        this.grossAmount = grossAmount.setScale(0, RoundingMode.HALF_UP);
                        this.gstRate = gstRate;
                        BigDecimal divisor = BigDecimal.ONE.add(gstRate.movePointLeft(2));
                        // One rounding step, straight to the rupee - rounding to paisa first would double-round.
                        this.netAmount = grossAmount.divide(divisor, 0, RoundingMode.HALF_UP).longValueExact();
                }

                /** Order id in the invoice-return flow, inventory item id in the debit-note flow. */
                public int getRefId() {
                        return refId;
                }

                public String getDescription() {
                        return description;
                }

                public long getQuantity() {
                        return quantity;
                }

                public long getGrossAmount() {
                        return grossAmount.longValueExact();
                }

                public BigDecimal getGstRate() {
                        return gstRate;
                }

                public long getNetAmount() {
                        return netAmount;
                }

                public String getFormattedGross() {
                        return FormattingUtils.formatDigit(getGrossAmount());
                }

                public String getFormattedNet() {
                        return FormattingUtils.formatDigit(netAmount);
                }

                public String getFormattedGstRate() {
                        return gstRate.stripTrailingZeros().toPlainString();
                }
        }

        private int purchaseReturnOrderId;
        private String flow;
        private String invoiceNumber;
        private int fofoId;
        private String partnerName;
        private String partnerCode;
        private String buyerGstin;
        private String reason;
        private String stage;
        private int attempts;
        private LocalDateTime lastAttemptAt;
        private String lastAttemptBy;
        private final List<Line> lines = new ArrayList<>();

        void addLine(int refId, String description, long quantity, BigDecimal grossAmount, BigDecimal gstRate) {
                lines.add(new Line(refId, description, quantity, grossAmount, gstRate));
        }

        public List<Line> getLines() {
                return lines;
        }

        public long getGrossTotal() {
                return lines.stream().mapToLong(Line::getGrossAmount).sum();
        }

        public long getRefundAmount() {
                return lines.stream().mapToLong(Line::getNetAmount).sum();
        }

        public long getGstRetained() {
                return getGrossTotal() - getRefundAmount();
        }

        public long getTotalQuantity() {
                return lines.stream().mapToLong(Line::getQuantity).sum();
        }

        public String getFormattedGrossTotal() {
                return FormattingUtils.formatDigit(getGrossTotal());
        }

        public String getFormattedRefundAmount() {
                return FormattingUtils.formatDigit(getRefundAmount());
        }

        public String getFormattedGstRetained() {
                return FormattingUtils.formatDigit(getGstRetained());
        }

        /** The B2C option stays open until the end of the calendar day of the latest refusal. */
        public LocalDateTime getAvailableUntil() {
                return lastAttemptAt == null ? null : lastAttemptAt.toLocalDate().atTime(LocalTime.of(23, 59, 59));
        }

        public boolean isAvailable() {
                return lastAttemptAt != null && lastAttemptAt.toLocalDate().equals(LocalDate.now());
        }

        public String getFormattedAvailableUntil() {
                return getAvailableUntil() == null ? "" : FormattingUtils.format(getAvailableUntil());
        }

        public String getFormattedLastAttemptAt() {
                return lastAttemptAt == null ? "" : FormattingUtils.format(lastAttemptAt);
        }

        public int getPurchaseReturnOrderId() {
                return purchaseReturnOrderId;
        }

        public void setPurchaseReturnOrderId(int purchaseReturnOrderId) {
                this.purchaseReturnOrderId = purchaseReturnOrderId;
        }

        public String getFlow() {
                return flow;
        }

        public void setFlow(String flow) {
                this.flow = flow;
        }

        public String getInvoiceNumber() {
                return invoiceNumber;
        }

        public void setInvoiceNumber(String invoiceNumber) {
                this.invoiceNumber = invoiceNumber;
        }

        public int getFofoId() {
                return fofoId;
        }

        public void setFofoId(int fofoId) {
                this.fofoId = fofoId;
        }

        public String getPartnerName() {
                return partnerName;
        }

        public void setPartnerName(String partnerName) {
                this.partnerName = partnerName;
        }

        public String getPartnerCode() {
                return partnerCode;
        }

        public void setPartnerCode(String partnerCode) {
                this.partnerCode = partnerCode;
        }

        public String getBuyerGstin() {
                return buyerGstin;
        }

        public void setBuyerGstin(String buyerGstin) {
                this.buyerGstin = buyerGstin;
        }

        public String getReason() {
                return reason;
        }

        public void setReason(String reason) {
                this.reason = reason;
        }

        public String getStage() {
                return stage;
        }

        public void setStage(String stage) {
                this.stage = stage;
        }

        public int getAttempts() {
                return attempts;
        }

        public void setAttempts(int attempts) {
                this.attempts = attempts;
        }

        public LocalDateTime getLastAttemptAt() {
                return lastAttemptAt;
        }

        public void setLastAttemptAt(LocalDateTime lastAttemptAt) {
                this.lastAttemptAt = lastAttemptAt;
        }

        public String getLastAttemptBy() {
                return lastAttemptBy;
        }

        public void setLastAttemptBy(String lastAttemptBy) {
                this.lastAttemptBy = lastAttemptBy;
        }
}