Skip to content

Commit 203e41c

Browse files
authored
feat: buyer ledger report generation (#9)
- buyer ledger report generation --------- Signed-off-by: Rajdeep Roy Chowdhury <[email protected]>
1 parent 9768f21 commit 203e41c

File tree

13 files changed

+286
-1
lines changed

13 files changed

+286
-1
lines changed

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ dependencies {
4242
implementation(libs.spring.boot.starter.data.jpa)
4343
implementation(libs.postgres.connector)
4444
implementation(libs.gson)
45+
implementation(libs.spring.boot.starter.freemarker)
46+
implementation(libs.openhtml)
4547

4648
compileOnly(libs.lombok)
4749
annotationProcessor(libs.lombok)

gradle/libs.versions.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ jackson = "2.15.0"
1212
micrometer = "1.11.0" # adjust if needed
1313
testcontainers = "1.17.6"
1414
rewritespring = "6.21.0"
15+
openhtml = "1.0.10"
1516

1617
[libraries]
1718
spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web" }
@@ -21,6 +22,7 @@ spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-
2122
spring-boot-starter-data-redis = { module = "org.springframework.boot:spring-boot-starter-data-redis", version.ref = "springboot" }
2223
spring-boot-starter-cache = { module = "org.springframework.boot:spring-boot-starter-cache", version = "2.4.3" }
2324
spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test" }
25+
spring-boot-starter-freemarker = { module = "org.springframework.boot:spring-boot-starter-freemarker" }
2426

2527
jjwt = { module = "io.jsonwebtoken:jjwt", version.ref = "jjwt" }
2628
jaxb-api = { module = "javax.xml.bind:jaxb-api", version.ref = "jaxb" }
@@ -42,3 +44,5 @@ testcontainers-mysql = { module = "org.testcontainers:mysql", version.ref = "tes
4244

4345
openrewrite-recipe-rewrite-spring = { module = "org.openrewrite.recipe:rewrite-spring", version.ref = "rewritespring" }
4446

47+
openhtml = { module = "com.openhtmltopdf:openhtmltopdf-pdfbox", version.ref = "openhtml"}
48+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.razdeep.konsignapi.controller;
2+
3+
import com.razdeep.konsignapi.constant.KonsignConstant;
4+
import com.razdeep.konsignapi.service.BuyerService;
5+
import com.razdeep.konsignapi.service.SupplierService;
6+
import io.micrometer.core.annotation.Timed;
7+
import org.springframework.http.MediaType;
8+
import org.springframework.http.ResponseEntity;
9+
import org.springframework.web.bind.annotation.*;
10+
11+
@CrossOrigin
12+
@RestController
13+
@RequestMapping(KonsignConstant.CONTROLLER_API_PREFIX + "/report")
14+
public class ReportController {
15+
16+
private final SupplierService supplierService;
17+
private final BuyerService buyerService;
18+
19+
public ReportController(SupplierService supplierService, BuyerService buyerService) {
20+
this.supplierService = supplierService;
21+
this.buyerService = buyerService;
22+
}
23+
24+
@Timed
25+
@GetMapping("/supplier")
26+
public ResponseEntity<byte[]> generateSupplierReport(@RequestParam String supplierId) throws Exception {
27+
final var bytes = supplierService.generateSupplierLedger(supplierId);
28+
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(bytes);
29+
}
30+
31+
@Timed
32+
@GetMapping("/buyer")
33+
public ResponseEntity<byte[]> generateBuyerReport(@RequestParam String buyerId) throws Exception {
34+
final var bytes = buyerService.generateBuyerLedger(buyerId);
35+
return ResponseEntity.ok().contentType(MediaType.APPLICATION_PDF).body(bytes);
36+
}
37+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.razdeep.konsignapi.repository;
2+
3+
import java.time.LocalDate;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Data;
6+
7+
@Data
8+
@AllArgsConstructor
9+
public class BillCollectionDTO {
10+
String billNo;
11+
LocalDate billDate;
12+
String billAmount;
13+
String supplierName;
14+
String voucherNo;
15+
Double amountCollected;
16+
String bank;
17+
String ddNo;
18+
LocalDate ddDate;
19+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.razdeep.konsignapi.repository;
2+
3+
import java.time.LocalDate;
4+
5+
public interface BillCollectionProjection {
6+
7+
String getBillNo();
8+
9+
LocalDate getBillDate();
10+
11+
String getBillAmount();
12+
13+
String getSupplierName();
14+
15+
String getVoucherNo();
16+
17+
Double getAmountCollected();
18+
19+
String getBank();
20+
21+
String getDdNo();
22+
23+
LocalDate getDdDate();
24+
}

src/main/java/com/razdeep/konsignapi/repository/BuyerRepository.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66
import lombok.NonNull;
77
import org.springframework.data.jpa.repository.JpaRepository;
8+
import org.springframework.data.jpa.repository.Query;
89
import org.springframework.stereotype.Repository;
910

1011
@Repository
@@ -14,4 +15,50 @@ public interface BuyerRepository extends JpaRepository<BuyerEntity, String> {
1415
List<BuyerEntity> findAllByAgencyId(@NonNull String agencyId);
1516

1617
Optional<BuyerEntity> findByBuyerIdAndAgencyId(@NonNull String buyerId, @NonNull String agencyId);
18+
19+
@Query("""
20+
select b.buyerName
21+
from BuyerEntity b
22+
where b.buyerId = :buyerId
23+
and b.agencyId = :agencyId
24+
""")
25+
String findBuyerNameByBuyerId(String buyerId, String agencyId);
26+
27+
@Query(value = """
28+
with collection_joined as (
29+
select
30+
*
31+
from
32+
collection_voucher
33+
join collection_voucher_item on
34+
voucher_no = fk_collection_voucher_id
35+
where buyer_buyer_id = :buyerId
36+
and collection_voucher.agency_id = :agencyId),
37+
bill_joined as (
38+
select
39+
*
40+
from
41+
bill
42+
join supplier on
43+
bill.fk_supplier_id = supplier.supplier_id
44+
join buyer on
45+
bill.fk_buyer_id = buyer.buyer_id
46+
)
47+
select
48+
bill_joined.bill_no as billNo,
49+
bill_joined.bill_date as billDate,
50+
bill_joined.bill_amount as billAmount,
51+
bill_joined.supplier_name as supplierName,
52+
collection_joined.voucher_no as voucherNo,
53+
collection_joined.amount_collected as amountCollected,
54+
collection_joined.bank as bank,
55+
collection_joined.dd_no as ddNo,
56+
collection_joined.dd_date as ddDate
57+
from
58+
bill_joined
59+
join collection_joined
60+
on
61+
bill_joined.bill_no = collection_joined.bill_bill_no;
62+
""", nativeQuery = true)
63+
List<BillCollectionProjection> computeBuyerLedger(@NonNull String buyerId, @NonNull String agencyId);
1764
}

src/main/java/com/razdeep/konsignapi/repository/SupplierRepository.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.List;
55
import java.util.Optional;
66
import org.springframework.data.jpa.repository.JpaRepository;
7+
import org.springframework.data.jpa.repository.Query;
78
import org.springframework.stereotype.Repository;
89

910
@Repository
@@ -14,4 +15,11 @@ public interface SupplierRepository extends JpaRepository<SupplierEntity, String
1415
List<SupplierEntity> findAllByAgencyId(String supplierName);
1516

1617
Optional<SupplierEntity> findSupplierBySupplierIdAndAgencyId(String supplierId, String agencyId);
18+
19+
@Query("""
20+
select s.supplierName
21+
from SupplierEntity s
22+
where s.supplierId = :supplierId
23+
""")
24+
String findSupplierNameBySupplierId(String supplierId);
1725
}

src/main/java/com/razdeep/konsignapi/service/BuyerService.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
import com.razdeep.konsignapi.entity.BuyerEntity;
44
import com.razdeep.konsignapi.model.Buyer;
5+
import com.razdeep.konsignapi.repository.BillCollectionDTO;
6+
import com.razdeep.konsignapi.repository.BillCollectionProjection;
57
import com.razdeep.konsignapi.repository.BuyerRepository;
68
import java.util.ArrayList;
9+
import java.util.HashMap;
710
import java.util.List;
11+
import java.util.Map;
812
import org.springframework.stereotype.Service;
913

1014
@Service
@@ -79,4 +83,29 @@ public BuyerEntity getBuyerByBuyerName(String buyerName) {
7983
final var resultList = buyerRepository.findAllBuyerByBuyerNameAndAgencyId(buyerName, agencyId);
8084
return resultList == null || resultList.isEmpty() ? null : resultList.get(0);
8185
}
86+
87+
public byte[] generateBuyerLedger(String buyerId) throws Exception {
88+
String agencyId = commonService.getAgencyId();
89+
Map<String, Object> payload = new HashMap<>();
90+
91+
String buyerName = buyerRepository.findBuyerNameByBuyerId(buyerId, agencyId);
92+
payload.put("buyerName", buyerName);
93+
94+
List<BillCollectionProjection> rows = buyerRepository.computeBuyerLedger(buyerId, agencyId);
95+
List<BillCollectionDTO> items = rows.stream()
96+
.map(rawRow -> new BillCollectionDTO(
97+
rawRow.getBillNo(),
98+
rawRow.getBillDate(),
99+
rawRow.getBillAmount(),
100+
rawRow.getSupplierName(),
101+
rawRow.getVoucherNo(),
102+
rawRow.getAmountCollected(),
103+
rawRow.getBank(),
104+
rawRow.getDdNo(),
105+
rawRow.getDdDate()))
106+
.toList();
107+
payload.put("items", items);
108+
109+
return commonService.generatePdf("buyer.ftl", payload);
110+
}
82111
}

src/main/java/com/razdeep/konsignapi/service/CommonService.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,27 @@
11
package com.razdeep.konsignapi.service;
22

3+
import com.openhtmltopdf.pdfboxout.PdfRendererBuilder;
34
import com.razdeep.konsignapi.model.KonsignUserDetails;
5+
import freemarker.template.Template;
6+
import freemarker.template.TemplateException;
7+
import java.io.ByteArrayOutputStream;
8+
import java.io.IOException;
9+
import java.io.StringWriter;
10+
import java.util.Map;
411
import org.springframework.security.core.context.SecurityContextHolder;
512
import org.springframework.stereotype.Service;
13+
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
614

715
@Service
816
public class CommonService {
917

1018
private static final int MAX_INITIAL_SIZE = 4;
1119

12-
public CommonService() {}
20+
private final FreeMarkerConfigurer freemarkerConfigurer;
21+
22+
public CommonService(FreeMarkerConfigurer freemarkerConfigurer) {
23+
this.freemarkerConfigurer = freemarkerConfigurer;
24+
}
1325

1426
private boolean isVowel(char c) {
1527
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'A' || c == 'E' || c == 'I' || c == 'O'
@@ -50,4 +62,35 @@ public String getAgencyId() {
5062
}
5163
return konsignUserDetails.getAgencyId();
5264
}
65+
66+
private byte[] convertHtmlToPdf(String html) throws Exception {
67+
ByteArrayOutputStream out = new ByteArrayOutputStream();
68+
69+
PdfRendererBuilder builder = new PdfRendererBuilder();
70+
builder.withHtmlContent(html, null);
71+
builder.toStream(out);
72+
73+
// IMPORTANT: Embed font
74+
// builder.useFont(
75+
// new File("fonts/DejaVuSans.ttf"),
76+
// "DejaVu Sans"
77+
// );
78+
79+
builder.run();
80+
return out.toByteArray();
81+
}
82+
83+
private String generateHtml(String templateName, Map<String, Object> payload)
84+
throws IOException, TemplateException {
85+
Template template = freemarkerConfigurer.getConfiguration().getTemplate(templateName);
86+
StringWriter writer = new StringWriter();
87+
template.process(payload, writer);
88+
89+
return writer.toString();
90+
}
91+
92+
public byte[] generatePdf(String templateName, Map<String, Object> payload) throws Exception {
93+
String html = generateHtml(templateName, payload);
94+
return convertHtmlToPdf(html);
95+
}
5396
}

src/main/java/com/razdeep/konsignapi/service/SupplierService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
import com.razdeep.konsignapi.model.Supplier;
55
import com.razdeep.konsignapi.repository.SupplierRepository;
66
import java.util.ArrayList;
7+
import java.util.HashMap;
78
import java.util.List;
9+
import java.util.Map;
810
import org.springframework.stereotype.Service;
911

1012
@Service
@@ -82,4 +84,13 @@ public SupplierEntity getSupplierBySupplierName(String supplierName) {
8284
final var resultList = supplierRepository.findAllSupplierBySupplierNameAndAgencyId(supplierName, agencyId);
8385
return resultList == null || resultList.isEmpty() ? null : resultList.get(0);
8486
}
87+
88+
// TODO: implement properly later
89+
public byte[] generateSupplierLedger(String supplierId) throws Exception {
90+
String supplierName = supplierRepository.findSupplierNameBySupplierId(supplierId);
91+
Map<String, Object> payload = new HashMap<>();
92+
payload.put("supplierName", supplierName);
93+
94+
return commonService.generatePdf("supplier.ftl", payload);
95+
}
8596
}

0 commit comments

Comments
 (0)