Subversion Repositories SmartDukaan

Rev

Rev 35799 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
35028 amit 1
package com.spice.profitmandi.common.util;// package com.spice.profitmandi.print; // <- uncomment and adjust if you want this in a package
2
 
3
import com.itextpdf.text.Document;
4
import com.itextpdf.text.DocumentException;
5
import com.itextpdf.text.Paragraph;
6
import com.itextpdf.text.pdf.PdfWriter;
7
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
8
import com.spice.profitmandi.common.model.CustomCustomer;
9
import com.spice.profitmandi.common.model.CustomOrderItem;
10
import com.spice.profitmandi.common.model.InvoicePdfModel;
11
 
12
import javax.print.*;
13
import java.io.ByteArrayOutputStream;
14
import java.nio.charset.Charset;
15
import java.util.ArrayList;
16
import java.util.List;
17
 
18
/**
19
 * Utility to format and print an InvoicePdfModel to a thermal printer width (58mm/80mm).
20
 * Compatible with Java 8.
21
 */
22
public class InvoiceFormatter {
23
 
24
    public static final int WIDTH_58MM = 32; // typical ~32 chars/line
25
    public static final int WIDTH_80MM = 48; // typical ~48 chars/line
26
 
27
    /**
28
     * Build printable lines for a thermal printer.
29
     */
30
    public static void getInvoice(InvoicePdfModel invoice, ByteArrayOutputStream baos, int lineWidth) throws ProfitMandiBusinessException {
31
        List<String> lines = new ArrayList<>();
32
 
33
        // ===== HEADER =====
34
        addIfNotEmpty(lines, center(safe(invoice.getTitle()), lineWidth));
35
        if (invoice.isCancelled()) {
36
            lines.add(center("*** INVOICE CANCELLED ***", lineWidth));
37
        }
36037 amit 38
        String docLabel = invoice.getTitle() != null && invoice.getTitle().toLowerCase().contains("challan") ? "DC No" : "Invoice No";
39
        addIfNotEmpty(lines, center(docLabel + ": " + safe(invoice.getInvoiceNumber()), lineWidth));
35028 amit 40
        addIfNotEmpty(lines, center("Date: " + safe(invoice.getInvoiceDate()), lineWidth));
41
        lines.add(repeat('-', lineWidth));
42
 
43
        // ===== CUSTOMER =====
44
        CustomCustomer c = invoice.getCustomer();
45
        if (c != null) {
46
            String fullName = (safe(c.getFirstName()) + " " + safe(c.getLastName())).trim();
47
            addIfNotEmpty(lines, "Customer: " + fullName);
48
            addIfNotEmpty(lines, "Mobile  : " + safe(c.getMobileNumber()));
49
            addIfNotEmpty(lines, "GSTIN   : " + safe(c.getGstNumber()));
50
 
51
            if (c.getAddress() != null) {
52
                List<String> addrLines = wrapWords("Address: " + c.getAddress().getAddressString(), lineWidth);
53
                lines.addAll(addrLines);
54
            }
55
            if (safe(invoice.getCustomerAddressStateCode()).length() > 0) {
56
                lines.add("State   : " + invoice.getCustomerAddressStateCode());
57
            }
58
            lines.add(repeat('-', lineWidth));
59
        }
60
 
61
        // ===== ITEMS HEADER =====
62
        // We'll allocate fixed widths for columns: Qty(4) Rate(9) Amount(10) -> trailer length ~ 4 + 1 + 9 + 1 + 10 = 25
63
        // Thus, description width = lineWidth - 25
64
        final String hdrTrailer = rightPad("Qty", 4) + " " + rightPad("Rate", 9) + " " + rightPad("Amount", 10);
65
        int descWidthForHeader = Math.max(0, lineWidth - hdrTrailer.length());
66
        lines.add(rightPad("Item", descWidthForHeader) + hdrTrailer);
67
        lines.add(repeat('-', lineWidth));
68
 
69
        // ===== ITEMS =====
70
        if (invoice.getOrderItems() != null) {
35799 amit 71
            boolean marginSeparatorAdded = false;
35028 amit 72
            for (CustomOrderItem item : invoice.getOrderItems()) {
35799 amit 73
                if (item != null && item.isMarginScheme() && !marginSeparatorAdded) {
74
                    lines.add(repeat('-', lineWidth));
75
                    lines.add(center("Margin Scheme - Rule 32(5)", lineWidth));
76
                    lines.add(repeat('-', lineWidth));
77
                    marginSeparatorAdded = true;
78
                }
35028 amit 79
                lines.addAll(formatItem(item, lineWidth));
80
            }
81
        }
82
        lines.add(repeat('-', lineWidth));
83
 
84
        // ===== TOTALS =====
85
        lines.add(right("TOTAL: " + formatAmount(invoice.getTotalAmount()), lineWidth));
86
 
87
        // T&Cs
88
        if (invoice.getTncs() != null && !invoice.getTncs().isEmpty()) {
89
            lines.add(repeat('-', lineWidth));
90
            lines.add("Terms & Conditions:");
91
            for (String t : invoice.getTncs()) {
92
                lines.addAll(wrapWords(safe(t), lineWidth));
93
            }
94
        }
95
 
35799 amit 96
        // ===== MARGIN SCHEME DECLARATION =====
97
        if (invoice.isHasMarginSchemeItems() && invoice.getMarginSchemeDeclarations() != null
98
                && !invoice.getMarginSchemeDeclarations().isEmpty()) {
99
            lines.add(repeat('-', lineWidth));
100
            lines.add("Margin Scheme Declaration:");
101
            for (String decl : invoice.getMarginSchemeDeclarations()) {
102
                lines.addAll(wrapWords("- " + safe(decl), lineWidth));
103
            }
104
        }
105
 
35028 amit 106
        lines.add(center("Thank you! Visit again", lineWidth));
107
        // Writer writes into the provided baos
108
        Document document = new Document();
109
 
110
        // Create PdfWriter that writes into baos
111
        try {
112
            PdfWriter.getInstance(document, baos);
113
        } catch (DocumentException e) {
114
            throw new ProfitMandiBusinessException("Could not Create instance", "Could not create instance", "Could not create instance");
115
        }
116
 
117
        document.open();
118
 
119
        // Add each string as a new line
120
        for (String line : lines) {
121
            try {
122
                document.add(new Paragraph(line));
123
            } catch (DocumentException e) {
124
                throw new RuntimeException(e);
125
            }
126
        }
127
 
128
        document.close(); // Very important: flushes to baos
129
 
130
    }
131
 
132
    public static void saveAsPdf(String filePath, List<String> lines) throws Exception {
133
        /*PdfWriter writer = new PdfWriter(filePath);
134
 
135
        // Create PDF document
136
        PdfDocument pdf = new PdfDocument(writer);
137
 
138
        // Add a document layout wrapper
139
        Document document = new Document(pdf);
140
 
141
        // Add each line as a paragraph
142
        for (String line : lines) {
143
            document.add(new Paragraph(line));
144
        }
145
 
146
        document.close();*/
147
    }
148
 
149
    /**
150
     * Format one item with word-wrapped description and aligned Qty/Rate/Amount.
151
     */
152
    public static List<String> formatItem(CustomOrderItem item, int lineWidth) {
153
        List<String> out = new ArrayList<String>();
154
        if (item == null) return out;
155
 
156
        // trailer columns: Qty(4), Rate(9 - incl. 2 decimals), Amount(10)
157
        String trailer = String.format("%4d %9.2f %10.2f",
158
                item.getQuantity(),
159
                item.getRate(),
160
                item.getNetAmount());
161
 
162
        int descWidth = Math.max(0, lineWidth - trailer.length());
163
        List<String> wrappedDesc = wrapWords(safe(item.getDescription()), descWidth);
164
        if (wrappedDesc.isEmpty()) wrappedDesc.add("");
165
 
166
        // First line: part of desc + trailer
167
        out.add(rightPad(wrappedDesc.get(0), descWidth) + trailer);
168
 
169
        // Remaining description lines
170
        for (int i = 1; i < wrappedDesc.size(); i++) {
171
            out.add(wrappedDesc.get(i));
172
        }
173
 
174
        // Optional meta lines under the item
175
        if (safe(item.getHsnCode()).length() > 0) {
176
            out.add("   HSN: " + item.getHsnCode());
177
        }
178
 
179
        float gstRate = safeFloat(item.getIgstRate()) + safeFloat(item.getCgstRate()) + safeFloat(item.getSgstRate());
180
        if (gstRate > 0.0f) {
181
            out.add(String.format("   GST: %.2f%% (IGST %.2f, CGST %.2f, SGST %.2f)",
182
                    gstRate, safeFloat(item.getIgstRate()), safeFloat(item.getCgstRate()), safeFloat(item.getSgstRate())));
183
        }
184
 
185
        if (safeFloat(item.getDiscount()) > 0.0f) {
186
            out.add("   Disc: " + formatAmount(item.getDiscount()));
187
        }
188
 
35799 amit 189
        if (item.isMarginScheme()) {
190
            out.add("   Purchase Price: " + formatAmount(item.getPurchasePrice()));
191
            out.add("   Selling Price : " + formatAmount(item.getSellingPrice()));
192
            out.add("   Margin (Taxable): " + formatAmount(item.getMargin()));
193
        }
194
 
35028 amit 195
        return out;
196
    }
197
 
198
// ===== Printing =====
199
 
200
    /**
201
     * Send lines to the DEFAULT system printer. Works if your thermal printer has a Windows/Linux driver.
202
     * If you need ESC/POS raw, see sendRawWithCut.
203
     */
204
    public static void sendToDefaultPrinter(List<String> lines) throws PrintException {
205
        PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
206
        if (svc == null) throw new PrintException("No default print service found");
207
        DocPrintJob job = svc.createPrintJob();
208
        StringBuilder sb = new StringBuilder();
209
        for (String line : lines) {
210
            sb.append(line == null ? "" : line).append('\n');
211
        }
212
        byte[] data = sb.toString().getBytes(Charset.forName("UTF-8"));
213
        Doc doc = new SimpleDoc(data, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
214
        job.print(doc, null);
215
    }
216
 
217
    /**
218
     * Attempt to send raw text + ESC/POS full cut to the default printer.
219
     * This only works if the printer driver accepts raw bytes. If you see junk, switch to a RAW queue or an ESC/POS lib.
220
     */
221
    public static void sendRawWithCut(List<String> lines) throws PrintException {
222
        PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
223
        if (svc == null) throw new PrintException("No default print service found");
224
        DocPrintJob job = svc.createPrintJob();
225
 
226
        // Build payload: text + some feeds + ESC/POS cut
227
        byte[] newline = new byte[]{'\n'};
228
        byte[] feed = new byte[]{0x1B, 'd', 0x05}; // ESC d 5  -> feed 5 lines
229
        byte[] cut = new byte[]{0x1D, 0x56, 0x42, 0x00}; // GS V B 0 -> full cut
230
 
231
        // join
232
        List<byte[]> chunks = new ArrayList<byte[]>();
233
        StringBuilder sb = new StringBuilder();
234
        for (String line : lines) {
235
            sb.append(line == null ? "" : line).append('\n');
236
        }
237
        chunks.add(sb.toString().getBytes(Charset.forName("UTF-8")));
238
        chunks.add(feed);
239
        chunks.add(cut);
240
 
241
        int total = 0;
242
        for (byte[] ch : chunks) total += ch.length;
243
        byte[] payload = new byte[total];
244
        int pos = 0;
245
        for (byte[] ch : chunks) {
246
            System.arraycopy(ch, 0, payload, pos, ch.length);
247
            pos += ch.length;
248
        }
249
 
250
        Doc doc = new SimpleDoc(payload, DocFlavor.BYTE_ARRAY.AUTOSENSE, null);
251
        job.print(doc, null);
252
    }
253
 
254
// ===== Helpers =====
255
 
256
    private static void addIfNotEmpty(List<String> list, String s) {
257
        if (s == null) return;
258
        if (s.trim().length() == 0) return;
259
        list.add(s);
260
    }
261
 
262
    private static String safe(String s) {
263
        return s == null ? "" : s;
264
    }
265
 
266
    private static float safeFloat(Float f) {
267
        return f == null ? 0.0f : f.floatValue();
268
    }
269
 
270
    private static String formatAmount(float v) {
271
        return String.format("%.2f", v);
272
    }
273
 
274
    private static String rightPad(String s, int width) {
275
        if (s == null) s = "";
276
        if (s.length() >= width) return s.substring(0, width);
277
        StringBuilder sb = new StringBuilder(s);
278
        while (sb.length() < width) sb.append(' ');
279
        return sb.toString();
280
    }
281
 
282
    private static String leftPad(String s, int width) {
283
        if (s == null) s = "";
284
        if (s.length() >= width) return s.substring(s.length() - width);
285
        StringBuilder sb = new StringBuilder();
286
        while (sb.length() + s.length() < width) sb.append(' ');
287
        sb.append(s);
288
        return sb.toString();
289
    }
290
 
291
    private static String center(String text, int width) {
292
        if (text == null) return "";
293
        if (text.length() >= width) return text;
294
        int pad = (width - text.length()) / 2;
295
        StringBuilder sb = new StringBuilder();
296
        for (int i = 0; i < pad; i++) sb.append(' ');
297
        sb.append(text);
298
        return sb.toString();
299
    }
300
 
301
    private static String right(String text, int width) {
302
        if (text == null) return "";
303
        if (text.length() >= width) return text;
304
        StringBuilder sb = new StringBuilder();
305
        for (int i = 0; i < width - text.length(); i++) sb.append(' ');
306
        sb.append(text);
307
        return sb.toString();
308
    }
309
 
310
    private static String repeat(char ch, int count) {
311
        StringBuilder sb = new StringBuilder();
312
        for (int i = 0; i < count; i++) sb.append(ch);
313
        return sb.toString();
314
    }
315
 
316
    private static String repeat(char ch, int count, boolean withNewline) {
317
        String s = repeat(ch, count);
318
        return withNewline ? s + "\n" : s;
319
    }
320
 
321
    private static String repeat(char ch1, char ch2, int count) {
322
        StringBuilder sb = new StringBuilder();
323
        for (int i = 0; i < count; i++) {
324
            sb.append(ch1).append(ch2);
325
        }
326
        return sb.toString();
327
    }
328
 
329
    private static String repeat(String s, int count) {
330
        StringBuilder sb = new StringBuilder();
331
        for (int i = 0; i < count; i++) sb.append(s);
332
        return sb.toString();
333
    }
334
 
335
    private static String repeat(char ch, int count, String trailer) {
336
        StringBuilder sb = new StringBuilder();
337
        for (int i = 0; i < count; i++) sb.append(ch);
338
        sb.append(trailer);
339
        return sb.toString();
340
    }
341
 
342
    private static String repeat(char ch, int count, String trailer, boolean newline) {
343
        String s = repeat(ch, count, trailer);
344
        return newline ? s + "\n" : s;
345
    }
346
 
347
    /**
348
     * Word wrap that prefers breaking at spaces; falls back to hard breaks for very long tokens.
349
     */
350
    public static List<String> wrapWords(String text, int width) {
351
        List<String> out = new ArrayList<String>();
352
        if (text == null) return out;
353
        text = text.trim();
354
        if (width <= 0) {
355
            out.add(text);
356
            return out;
357
        }
358
 
359
        String[] words = text.split("\\s+");
360
        StringBuilder line = new StringBuilder();
361
        int i;
362
        for (i = 0; i < words.length; i++) {
363
            String w = words[i];
364
            if (w.length() > width) {
365
                // Flush current line
366
                if (line.length() > 0) {
367
                    out.add(line.toString());
368
                    line.setLength(0);
369
                }
370
                // Hard wrap the long word
371
                int idx = 0;
372
                while (idx < w.length()) {
373
                    int end = Math.min(idx + width, w.length());
374
                    out.add(w.substring(idx, end));
375
                    idx = end;
376
                }
377
                continue;
378
            }
379
            if (line.length() == 0) {
380
                line.append(w);
381
            } else if (line.length() + 1 + w.length() <= width) {
382
                line.append(' ').append(w);
383
            } else {
384
                out.add(line.toString());
385
                line.setLength(0);
386
                line.append(w);
387
            }
388
        }
389
        if (line.length() > 0) out.add(line.toString());
390
        return out;
391
    }
392
}