package service;
import api.v1.ForecastSearchResult;
import api.v1.InvoiceData;
import com.avaje.ebean.Ebean;
import exceptions.PoseidonException;
import models.*;
import org.joda.time.DateTime;
import play.mvc.Http;
import java.text.SimpleDateFormat;
import java.util.*;
public class InvoiceGeneratorService extends PoseidonService {
private ProductModel defaultProduct ;
private final ProductModel oppstartsgebyrProduct;
public InvoiceGeneratorService(String user) {
super(user);
defaultProduct = ProductModel.findByName("Offshore by MET.no");
if ( defaultProduct == null) {
throw new PoseidonException(Http.Status.INTERNAL_SERVER_ERROR,"Fant ikke default produkt");
}
oppstartsgebyrProduct = ProductModel.findBySku("oppstart");
if ( oppstartsgebyrProduct == null) {
throw new PoseidonException(Http.Status.INTERNAL_SERVER_ERROR,"Fant ikke produkt for oppstartgebyr");
}
}
public String generateInvoice(List<InvoiceLine> invoiceData) {
String CSV_COLUMN_HEADER = "BRUDD\tFORETAK\tRESKNR\tPERIODE\tBILDATO\tFORDATO\tBETBET\tVARREF\tKUNREF\tORDRE\tVARENR\tTEKST\tLKTODEL2\tLKTODEL3\tLKTODEL6\tVALKODE\tANTALL\tSATS\tBELOP\tAVGKODE\n";
StringBuilder builder = new StringBuilder(CSV_COLUMN_HEADER);
for (InvoiceLine invoiceLine : invoiceData) {
builder
.append(invoiceLine.brudd).append("\t")
.append(invoiceLine.foretak).append("\t")
.append(invoiceLine.resknr).append("\t")
.append(invoiceLine.periode).append("\t")
.append(invoiceLine.bildato).append("\t")
.append("").append("\t") // fordato
.append("").append("\t") // betbet
.append(invoiceLine.varref).append("\t")
.append(invoiceLine.kunref).append("\t")
.append(invoiceLine.ordre).append("\t")
.append(invoiceLine.varenr).append("\t")
.append(invoiceLine.tekst).append("\t")
.append(invoiceLine.lktodel2).append("\t")
.append(invoiceLine.lktodel3).append("\t")
.append("").append("\t") // lktodel6
.append("").append("\t") // valkode
.append(invoiceLine.antall).append("\t")
.append(invoiceLine.sats).append("\n");
}
return builder.toString();
}
public List<InvoiceLine> generateInvoiceLines(InvoiceData data, String ourReference) {
StandardAccountModel k2 = StandardAccountModel.findByName("K2");
StandardAccountModel k3 = StandardAccountModel.findByName("K3");
String varenr = PoseidonPropertyService.getProperty("invoicing.varenr");
int oppstartsgebyr = oppstartsgebyrProduct.price;
SimpleDateFormat sdfPeriode = new SimpleDateFormat("yyMM");
SimpleDateFormat sdfBilldate = new SimpleDateFormat("yyyyddMM");
DateTime now = getNow();
String periode = sdfPeriode.format(now.toDate());
String bildato = sdfBilldate.format(now.toDate());
String varref = ourReference;
Map<String, InvoiceableCustomer> customerMap = mapForecastsToCustomersAndOrders(data);
List<InvoiceLine> result = mapCustomerOrderForecastsToInvoiceLines(customerMap, oppstartsgebyr, periode, bildato, varref,varenr, k2,k3);
return result;
}
private Map<String, InvoiceableCustomer> mapForecastsToCustomersAndOrders(InvoiceData data) {
Map<String, InvoiceableCustomer> result = new HashMap<>();
for (ForecastSearchResult forecast : data.forecasts) {
OrderModel order = OrderModel.findByMetref(forecast.met_ref);
String customerNumber = order.customer.customer_number;
if ( customerNumber == null) continue; // ignorer kunder uten kundenummer
InvoiceableCustomer customer;
if (result.containsKey(customerNumber)) { // customer exists in map
customer = result.get(customerNumber);
Map<String, InvoiceableOrder> orders = customer.orders;
InvoiceableOrder invoiceableOrder;
if (orders.containsKey(order.met_ref)) {
invoiceableOrder = orders.get(order.met_ref);
} else {
invoiceableOrder = new InvoiceableOrder(order);
orders.put(order.met_ref, invoiceableOrder);
}
invoiceableOrder.producedForecasts = forecast.produced_forecasts;
invoiceableOrder.start = forecast.start_date;
invoiceableOrder.end = forecast.end_date;
} else {
customer = new InvoiceableCustomer(customerNumber);
result.put(customerNumber, customer);
InvoiceableOrder invoiceableOrder = new InvoiceableOrder(order);
invoiceableOrder.producedForecasts = forecast.produced_forecasts;
invoiceableOrder.start = forecast.start_date;
invoiceableOrder.end = forecast.end_date;
customer.orders.put(order.met_ref,invoiceableOrder);
}
}
return result;
}
private List<InvoiceLine> mapCustomerOrderForecastsToInvoiceLines(Map<String, InvoiceableCustomer> customerMap, int oppstartsgebyr, String periode, String bildato, String varref, String varenr, StandardAccountModel k2, StandardAccountModel k3) {
List<InvoiceLine> result = new ArrayList<>();
for (String customerNumber : customerMap.keySet()) {
InvoiceableCustomer customer = customerMap.get(customerNumber);
for (String met_ref : customer.orders.keySet()) {
InvoiceableOrder invoiceableOrder = customer.orders.get(met_ref);
if (invoiceableOrder != null) {
int antallVarsel = invoiceableOrder.producedForecasts ;
OrderModel order = invoiceableOrder.orderModel;
result.add(getReminderLine(order, periode, bildato, varref));
if (order.startup_fee) {
result.add(getStartupFeeLine(order, periode, bildato, varref, oppstartsgebyr,varenr,k2,k3));
}
String pNavn = order.position_name != null && !order.position_name.isEmpty() ? order.position_name : order.position.name;
String tekst = getTekstFakturalinje(pNavn, invoiceableOrder);
result.add(getForecastFeeLine(order, periode, bildato, varref, tekst, antallVarsel,varenr,k2,k3));
}
}
}
return result;
}
private String getTekstFakturalinje(String positionName,InvoiceableOrder order) {
SimpleDateFormat sdf = new SimpleDateFormat("yyMM");
return "Varsel for " + positionName + " fom. " + sdf.format(order.start) + " tom. " + sdf.format(order.end);
}
private InvoiceLine getReminderLine(OrderModel order, String periode, String bildato, String varref) {
InvoiceLine line = new InvoiceLine();
line.resknr = order.customer.customer_number;
line.periode = periode;
line.bildato = bildato;
line.varref = varref;
line.kunref = order.custref_po_calloff;
line.ordre = order.met_ref;
line.varenr = "T";
line.lktodel2 = "";
line.lktodel3 = "";
line.tekst = "Vær vennlig å oppgi fakturanummer ved betaling";
line.antall = 1;
return line;
}
private InvoiceLine getStartupFeeLine(OrderModel order, String periode, String bildato, String varref, int oppstartsgebyr, String varenr, StandardAccountModel k2, StandardAccountModel k3) {
InvoiceLine line = new InvoiceLine();
line.resknr = order.customer.customer_number;
line.periode = periode;
line.bildato = bildato;
line.varref = varref;
line.varenr = varenr;
line.kunref = order.custref_po_calloff;
line.ordre = order.met_ref;
line.lktodel2 = k2.account;
line.lktodel3 = k3.account;
line.antall = 1;
line.tekst ="Oppstartsgebyr";
line.sats = oppstartsgebyr;
return line;
}
private InvoiceLine getForecastFeeLine(OrderModel order, String periode, String bildato, String varref, String tekst, int antallVarsel, String varenr, StandardAccountModel k2, StandardAccountModel k3) {
InvoiceLine line = new InvoiceLine();
line.resknr = order.customer.customer_number;
line.periode = periode;
line.bildato = bildato;
line.varref = varref;
line.varenr = varenr;
line.kunref = order.custref_po_calloff;
line.ordre = order.met_ref;
line.lktodel2 = k2.account;
line.lktodel3 = k3.account;
line.tekst = tekst;
if ( order.product != null ) {
line.sats = order.product.price;
} else {
line.sats = defaultProduct.price;
}
line.antall = antallVarsel;
return line;
}
public void markForecastsAsBilled(ForecastSearchResult[] forecasts) {
for (int i = 0; i < forecasts.length; i++) {
ForecastSearchResult forecast = forecasts[i];
List<ForecastModel> forecastModels = ForecastModel.findByMetrefAndDateRange(forecast.met_ref, forecast.start_date, forecast.end_date);
for (ForecastModel forecastModel : forecastModels) {
forecastModel.invoiced = true;
forecastModel.updated = getNow().toDate();
forecastModel.updatedBy = getUser();
Ebean.update(forecastModel);
}
}
}
private class InvoiceableCustomer {
public String customerNumber;
public Map<String, InvoiceableOrder> orders = new HashMap<>();
public InvoiceableCustomer(String customerNumber) {
this.customerNumber = customerNumber;
}
}
private class InvoiceableOrder {
public OrderModel orderModel;
public int producedForecasts;
public Date start;
public Date end;
public InvoiceableOrder(OrderModel orderModel) {
this.orderModel = orderModel;
}
}
}