Skip to content

Commit

Permalink
refactor monthly reporting and add invoice entity
Browse files Browse the repository at this point in the history
  • Loading branch information
jdbranham committed Jun 17, 2024
1 parent 0bebb1d commit 5e915e0
Show file tree
Hide file tree
Showing 19 changed files with 666 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,25 @@
import java.util.Comparator;
import java.util.UUID;

import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Transient;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.annotation.ActionLayout;
import org.apache.causeway.applib.annotation.Bounding;
import org.apache.causeway.applib.annotation.Collection;
import org.apache.causeway.applib.annotation.DomainObject;
import org.apache.causeway.applib.annotation.DomainObjectLayout;
import org.apache.causeway.applib.annotation.Editing;
import org.apache.causeway.applib.annotation.Navigable;
import org.apache.causeway.applib.annotation.Property;
import org.apache.causeway.applib.annotation.PropertyLayout;
import org.apache.causeway.applib.annotation.Publishing;
import org.apache.causeway.applib.annotation.Title;
import org.apache.causeway.applib.annotation.Where;
import org.apache.causeway.applib.jaxb.PersistentEntityAdapter;
import org.apache.causeway.applib.services.message.MessageService;
import org.apache.causeway.applib.services.repository.RepositoryService;
import org.apache.causeway.applib.services.title.TitleService;
import org.apache.causeway.persistence.jpa.applib.integration.CausewayEntityListener;

import jakarta.inject.Named;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -39,7 +32,6 @@
import net.savantly.nexus.audited.dom.AuditedEntity;
import net.savantly.nexus.organizations.OrganizationsModule;
import net.savantly.nexus.organizations.dom.organization.Organization;
import net.savantly.nexus.products.dom.product.Product;

@Named(OrganizationsModule.NAMESPACE + ".Invoice")
@jakarta.persistence.Entity
Expand All @@ -52,16 +44,6 @@
@ToString(onlyExplicitlyIncluded = true)
public class Invoice extends AuditedEntity implements Comparable<Invoice> {

@Inject
@Transient
RepositoryService repositoryService;
@Inject
@Transient
TitleService titleService;
@Inject
@Transient
MessageService messageService;

public static Invoice withRequiredFields(String id, Organization organization, LocalDate startDate, LocalDate endDate) {
val entity = new Invoice();
entity.id = id;
Expand All @@ -70,10 +52,26 @@ public static Invoice withRequiredFields(String id, Organization organization, L
}

public static Invoice withRequiredFields(Organization organization, LocalDate startDate, LocalDate endDate) {
val id = UUID.randomUUID().toString();
val id = UUID.randomUUID().toString().substring(0, 16);
return withRequiredFields(id, organization, startDate, endDate);
}

public static Invoice withRequiredFields(MonthlyOrgReport report) {
var id = UUID.randomUUID().toString().substring(0, 16);
var startDate = LocalDate.of(report.getYear(), report.getMonth().getValue(), 1);
var endDate = startDate.plusMonths(1).minusDays(1);
var entity = withRequiredFields(id, report.getOrganization(), startDate, endDate);

report.getLineItems().forEach(li -> {
var lineItem = InvoiceLineItem.withRequiredFields(entity);
lineItem.setProductBillingAmount(li.getProductBillingAmount());
lineItem.setProductName(li.getProductName());
lineItem.setProductQuantity(li.getProductQuantity());
entity.getLineItems().add(lineItem);
});
return entity;
}

// *** PROPERTIES ***

@Id
Expand All @@ -96,7 +94,7 @@ public static Invoice withRequiredFields(Organization organization, LocalDate st
@PropertyLayout(fieldSetId = "identity", sequence = "2", navigable = Navigable.PARENT, hidden = Where.ALL_TABLES)
private Organization organization;

@Column(name = "start_date")
@Column(name = "invoice_year")
@PropertyLayout(fieldSetId = "identity", sequence = "4")
@Getter
private LocalDate startDate;
Expand All @@ -106,6 +104,13 @@ public static Invoice withRequiredFields(Organization organization, LocalDate st
@Getter
private LocalDate endDate;

@Collection
@Getter
@PropertyLayout(fieldSetId = "lineItems", sequence = "1")
@OneToOne(mappedBy = "invoice")
@JoinColumn(name = "invoice_id")
private java.util.SortedSet<InvoiceLineItem> lineItems = new java.util.TreeSet<>();


// *** ACTIONS ***

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package net.savantly.nexus.orgfees.dom.invoice;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.UUID;

import org.apache.causeway.applib.annotation.Bounding;
import org.apache.causeway.applib.annotation.DomainObject;
import org.apache.causeway.applib.annotation.DomainObjectLayout;
import org.apache.causeway.applib.annotation.Editing;
import org.apache.causeway.applib.annotation.Property;
import org.apache.causeway.applib.annotation.PropertyLayout;
import org.apache.causeway.applib.annotation.Publishing;
import org.apache.causeway.applib.annotation.Where;
import org.apache.causeway.applib.jaxb.PersistentEntityAdapter;
import org.apache.causeway.persistence.jpa.applib.integration.CausewayEntityListener;

import jakarta.inject.Named;
import jakarta.persistence.Column;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.val;
import net.savantly.nexus.organizations.OrganizationsModule;

@Named(OrganizationsModule.NAMESPACE + ".InvoiceLineItem")
@jakarta.persistence.Entity
@jakarta.persistence.Table(schema = OrganizationsModule.SCHEMA)
@jakarta.persistence.EntityListeners(CausewayEntityListener.class)
@DomainObject(entityChangePublishing = Publishing.ENABLED, editing = Editing.DISABLED, bounding = Bounding.BOUNDED)
@DomainObjectLayout(cssClassFa = "file-lines", named = "Invoice Line Item")
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@XmlJavaTypeAdapter(PersistentEntityAdapter.class)
@ToString(onlyExplicitlyIncluded = true)
public class InvoiceLineItem implements Comparable<InvoiceLineItem> {

public static InvoiceLineItem withRequiredFields(String id, Invoice invoice) {
val entity = new InvoiceLineItem();
entity.id = id;
entity.invoice = invoice;

return entity;
}

public static InvoiceLineItem withRequiredFields(Invoice invoice) {
val id = UUID.randomUUID().toString();
return withRequiredFields(id, invoice);
}

// *** PROPERTIES ***

@Id
@Column(name = "id", nullable = false)
@Getter
private String id;

@jakarta.persistence.Version
@jakarta.persistence.Column(name = "version", nullable = false)
@PropertyLayout(fieldSetId = "metadata", sequence = "999", hidden = Where.PARENTED_TABLES)
@Getter
@Setter
private long version;

@Getter
@Property
@PropertyLayout(fieldSetId = "identity")
@ManyToOne
private Invoice invoice;

@Column(name = "purchase_date")
@PropertyLayout(fieldSetId = "identity")
@Getter
@Setter
private LocalDate purchaseDate;

@Getter
@Setter
@Property
@PropertyLayout(fieldSetId = "identity")
private String productName;

@Getter
@Setter
@Property
@PropertyLayout(fieldSetId = "identity")
private String productDescription;

@Getter
@Setter
@Property
@PropertyLayout(fieldSetId = "identity")
private String productBillingInterval;

@Getter
@Setter
@Property
@PropertyLayout(fieldSetId = "identity")
private double productBillingAmount;

@Getter
@Setter
@Property
@PropertyLayout(fieldSetId = "identity")
private double productQuantity;


private final static Comparator<InvoiceLineItem> comparator = Comparator.comparing(s -> s.purchaseDate);

@Override
public int compareTo(final InvoiceLineItem other) {
return comparator.compare(this, other);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package net.savantly.nexus.orgfees.dom.invoice;

import java.util.Set;

import org.springframework.data.jpa.repository.JpaRepository;

public interface InvoiceRepository extends JpaRepository<Invoice, String>{

Set<Invoice> findByOrganizationId(String id);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package net.savantly.nexus.orgfees.dom.invoice;


import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.annotation.ActionLayout;
import org.apache.causeway.applib.annotation.PriorityPrecedence;
import org.apache.causeway.applib.annotation.PromptStyle;
import org.apache.causeway.applib.annotation.SemanticsOf;
import org.apache.causeway.applib.services.message.MessageService;
import org.apache.causeway.applib.services.repository.RepositoryService;

import jakarta.inject.Inject;
import jakarta.persistence.Transient;
import net.savantly.nexus.organizations.dom.organization.Organization;

@Action(semantics = SemanticsOf.IDEMPOTENT_ARE_YOU_SURE)
@jakarta.annotation.Priority(PriorityPrecedence.EARLY)
@lombok.RequiredArgsConstructor(onConstructor_ = { @Inject })
public class Invoice_delete {

final Invoice object;

public static class ActionEvent
extends
org.apache.causeway.applib.CausewayModuleApplib.ActionDomainEvent<Invoice_delete> {
}

@Inject
@Transient
RepositoryService repositoryService;
@Inject
@Transient
MessageService messageService;

@ActionLayout(named = "Delete Invoice", promptStyle = PromptStyle.DIALOG)
public Organization act() {
var organization = object.getOrganization();
repositoryService.remove(object);
messageService.informUser("Deleted " + object);
return organization;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.ArrayList;
import java.util.List;

import org.apache.causeway.applib.annotation.Action;
import org.apache.causeway.applib.annotation.ActionLayout;
import org.apache.causeway.applib.annotation.Bounding;
import org.apache.causeway.applib.annotation.Collection;
import org.apache.causeway.applib.annotation.CollectionLayout;
Expand Down Expand Up @@ -39,6 +41,8 @@
import net.savantly.nexus.orgfees.dom.onetime.OneTimePurchaseRepository;
import net.savantly.nexus.orgfees.dom.subscription.Subscription;
import net.savantly.nexus.orgfees.dom.subscription.SubscriptionRepository;
import net.savantly.nexus.products.dom.product.Product;
import net.savantly.nexus.products.dom.product.Products;

@DomainObject(nature = Nature.VIEW_MODEL, editing = Editing.DISABLED, bounding = Bounding.BOUNDED)
@Named(OrganizationsModule.NAMESPACE + ".MonthlyOrgReport")
Expand All @@ -51,7 +55,6 @@
"organization",
"year",
"month",
"totalAmount",
})
@XmlAccessorType(XmlAccessType.FIELD)
public class MonthlyOrgReport {
Expand All @@ -66,6 +69,11 @@ public class MonthlyOrgReport {
@XmlTransient
SubscriptionRepository subscriptionRepository;

@Inject
@Transient
@XmlTransient
Products products;

public static MonthlyOrgReport withRequiredFields(Organization organization, MonthType month, int year) {
log.info("Creating MonthlyOrgReport for {} {} {}", organization.getName(), month, year);
var object = new MonthlyOrgReport();
Expand Down Expand Up @@ -103,12 +111,13 @@ public String getTitle() {
@XmlElement(required = true)
private int year;

@Getter
@Setter
@XmlTransient
@Transient
@Property
@PropertyLayout(fieldSetId = "identity")
@XmlElement(required = true)
private double totalAmount;
public double getTotalAmount() {
return getLineItems().stream().mapToDouble(MonthlyOrgReportItem::getTotalAmount).sum();
}

@XmlTransient
@Transient
Expand All @@ -132,21 +141,35 @@ public java.util.Collection<MonthlyOrgReportItem> getLineItems() {
}

private MonthlyOrgReportItem fromOneTimePurchase(OneTimePurchase otp) {
double productPrice = calculatePriceFromProduct(otp.getProduct());
double exactAmount = productPrice * otp.getQuantity();
double roundedTotalAmount = Math.round(exactAmount * 100.0) / 100.0;

return new MonthlyOrgReportItem()
.setOrganizationName(organization.getName())
.setProductBillingAmount(otp.getProduct().getPrice())
.setProductBillingAmount(calculatePriceFromProduct(otp.getProduct()))
.setProductBillingInterval("ONE-TIME")
.setProductDescription(otp.getProduct().getDescription())
.setProductName(otp.getProduct().getName());
.setProductName(otp.getProduct().getName())
.setProductQuantity(otp.getQuantity())
.setTotalAmount(roundedTotalAmount);
}

private MonthlyOrgReportItem fromSubscription(Subscription s) {
double productPrice = calculatePriceFromProduct(s.getProduct());
double totalAmount = productPrice * 1;
return new MonthlyOrgReportItem()
.setOrganizationName(organization.getName())
.setProductBillingAmount(s.getProduct().getPrice())
.setProductBillingAmount(productPrice)
.setProductBillingInterval(s.getProduct().getBillingInterval().name())
.setProductDescription(s.getProduct().getDescription())
.setProductName(s.getProduct().getName());
.setProductName(s.getProduct().getName())
.setProductQuantity(1)
.setTotalAmount(totalAmount);
}

private double calculatePriceFromProduct(Product product) {
return products.calculatePriceAtDate(product, LocalDate.of(year, month.getValue(), 1));
}

}
Loading

0 comments on commit 5e915e0

Please sign in to comment.