simonbengtsson / jsPDF-AutoTable

jsPDF plugin for generating PDF tables with javascript
https://simonbengtsson.github.io/jsPDF-AutoTable/
MIT License
2.33k stars 624 forks source link

Multitable loop doesn't align properly and creates unusual gaps or overlaps each other #1050

Closed thediveshsharma closed 6 months ago

thediveshsharma commented 7 months ago

bankReportAxisBank2024-04-10.pdf

This is my output pdf and this is my code please help me so that deposit and withdrawal table are always paralled , no matter which table is long and there are no gaps if any table moves to a new page due to more content.

CODE:= import { format } from "date-fns"; import jsPDF from "jspdf"; import "jspdf-autotable"; import { useState } from "react"; import { Button, Col, Container, FormGroup, Input, Label, TextArea, } from "reactstrap";

const Pdf = ({ entries, unitList, selectedBankName, selectedDate, currentDate, openingBalances, closingBalances, }) => { const [additionalComments, setAdditionalComments] = useState(""); var doc = new jsPDF();

const giveEachUnitData = (data) => { // Define the desired sequence of unit names const desiredSequence = ["Flexible", "Printing", "Duplex", "Noida"];

// Initialize an object to hold unit data
const unitData = {};

// Iterate over the desired sequence
desiredSequence.forEach((unitName) => {
  // Find matching data for the current unitName
  const matchingData = data.filter((item) => item.unitName === unitName);

  // If there is matching data, add it to unitData
  if (matchingData.length > 0) {
    unitData[unitName] = matchingData;
  }
});

// Add remaining data not in the desired sequence to unitData
data.forEach((item) => {
  if (!unitData[item.unitName]) {
    unitData[item.unitName] = [item];
  }
});

return unitData;

};

const createTable = (entries) => { // Initialize startY for the new table let startY = 10; const bankHeading = selectedBankName + " " + "Report Dated" + " " + (selectedDate ? format(selectedDate, "dd-MM-yyyy") : format(currentDate, "dd-MM-yyyy")); doc.setFontSize(12);

doc.text(bankHeading, 220, startY + 10);

// Add new table for overall summary
doc.text("Unit Balance Summary", 14, startY + 20);
const overallSummaryHeaders = [
  "Unit Name",
  "Opening Balance",
  "Total Deposits",
  "Total Withdrawals",
  "Closing Balance",
];

// Merge entries and unitList
const mergedList = [];

// Loop through all units in unitList
unitList.forEach((unit) => {
  // Check if there's an entry for the unit
  const unitEntry = entries.find(
    (entry) => entry.unitName === unit.unitName
  );

  // If there's an entry, push it to the mergedList
  if (unitEntry) {
    const mergedEntry = {
      ...unitEntry,
      openingBalance: openingBalances[unit.unitName] || 0,
      closingBalance: closingBalances[unit.unitName] || 0,
    };
    mergedList.push(mergedEntry);
  } else {
    // If there's no entry, create a new entry with opening and closing balances from openingBalances and closingBalances
    mergedList.push({
      unitName: unit.unitName,
      openingBalance: openingBalances[unit.unitName] || 0, // Set opening balance to 0 if not available
      deposits: [], // Set deposits to empty array
      withdrawals: [], // Set withdrawals to empty array
      closingBalance: closingBalances[unit.unitName] || 0, // Set closing balance to 0 if not available
    });
  }
});

// Sort the merged list based on the desired sequence defined in unitList
mergedList.sort((a, b) => {
  const sequenceOrder = ["Flexible", "Printing", "Duplex", "Noida"];
  const aIndex = sequenceOrder.indexOf(a.unitName);
  const bIndex = sequenceOrder.indexOf(b.unitName);
  if (aIndex !== -1 && bIndex !== -1) {
    return aIndex - bIndex;
  } else if (aIndex !== -1) {
    return -1;
  } else if (bIndex !== -1) {
    return 1;
  } else {
    return 0;
  }
});

// Generate the overall summary data based on the sorted merged list
const overallSummaryData = mergedList.map((entry) => [
  entry.unitName,
  parseFloat(entry.openingBalance / 100000).toFixed(1),
  entry.deposits
    .reduce((acc, curr) => acc + parseFloat(curr.amount / 100000), 0)
    .toFixed(1),
  entry.withdrawals
    .reduce((acc, curr) => acc + parseFloat(curr.amount / 100000), 0)
    .toFixed(1),
  parseFloat(entry.closingBalance / 100000).toFixed(1),
]);

doc.autoTable({
  head: [overallSummaryHeaders],
  body: overallSummaryData,
  startY: startY + 25,
  margin: { left: 14, right: 14 },
  theme: "grid",
  styles: { overflow: "linebreak", fontSize: 8 },

  didDrawPage: (HookData) => {
    startY = HookData.cursor.y;
  },
});

// filter all unitNames and their data
const unitData = giveEachUnitData(entries);

let first = null,
  last = null;
let y = 160;
let pageNumber = 1;
for (const unitName in unitData) {
  const singleUnitData = unitData[unitName];

  const deposits = singleUnitData[0].deposits;
  const withdrawals = singleUnitData[0].withdrawals;

  const depositTotal = deposits
    .reduce((sum, item) => sum + parseFloat(item.amount), 0)
    .toLocaleString("en-IN", { maximumFractionDigits: 2 });

  const withdrawalTotal = withdrawals
    .reduce((sum, item) => sum + parseFloat(item.amount), 0)
    .toLocaleString("en-IN", { maximumFractionDigits: 2 });

  y =
    doc.autoTable.previous.finalY !== 131
      ? first.finalY > last.finalY
        ? first.finalY + 25
        : last.finalY + 25
      : y;
  y = y >= 450 ? y + 50 : y;
  const currentPageNumber = doc.internal.getCurrentPageInfo().pageNumber;

  if (currentPageNumber !== pageNumber) {
    // Set the page back to the original page number if it has changed
    doc.setPage(pageNumber);
  }
  // const previousPageNumber = pageNumber;
  // pageNumber = doc.internal.getNumberOfPages();
  // const pageChanged = pageNumber !== previousPageNumber;

  // Adjust y position only if the previous table went to a new page
  // if (pageChanged) {
  //   y = doc.autoTable.previous.finalY + 25; // Adjust the value as needed
  // }
  doc.text(unitName, 250, y);
  doc.autoTable({
    head: [
      [
        {
          content: "Deposits",
          colSpan: 2,
          styles: {
            halign: "center",
            fillColor: false,
            textColor: "#000000",
          },
        },
      ],
      [{ content: "Party Name" }, { content: "Amount" }],
    ],

    body: [
      ...deposits.map((item) => [`${item.party}`, `${item.amount}`]),
      // ["Total", depositTotal],
      [
        { content: "Total", styles: { fontStyle: "bold" } },
        { content: depositTotal, styles: { fontStyle: "bold" } },
      ],
    ],
    startY: parseInt(y),

    styles: {
      cellWidth: "wrap",
      valign: "middle",
      font: "times",
      fontSize: 8,
      overflow: "linebreak",
    },
    tableWidth: 250,
    headStyles: {
      fillColor: [247, 185, 7],
    },
    theme: "striped",
    // margin: {
    //   top: 50,
    //   right: 0,
    // },
    styles: { overflow: "linebreak" },
    margin: { right: 307 },
  });

  first = doc.autoTable.previous;
  doc.setPage(pageNumber);

  doc.autoTable({
    head: [
      [
        {
          content: "Withdrawals",
          colSpan: 2,
          styles: {
            halign: "center",
            fillColor: false,
            textColor: "#000000",
          },
        },
      ],
      [{ content: "Party Name" }, { content: "Amount" }],
    ],

    body: [
      ...withdrawals.map((item) => [`${item.party}`, `${item.amount}`]),

      [
        { content: "Total", styles: { fontStyle: "bold" } },
        { content: withdrawalTotal, styles: { fontStyle: "bold" } },
      ],
    ],
    startY: parseInt(y),
    styles: {
      cellWidth: "wrap",
      valign: "middle",
      font: "times",
      fontSize: 8,
      overflow: "linebreak",
    },
    tableWidth: 250,
    headStyles: {
      fillColor: [247, 185, 7],
    },
    theme: "striped",
    // margin: {
    //   top: 50,
    //   left: 0,
    // },
    styles: { overflow: "linebreak" },
    margin: { left: 307 },
  });

  last = doc.autoTable.previous;
}

};

const downloadIt = () => { doc = new jsPDF("1", "pt"); // Reset the PDF document createTable(entries); //set filename const documentName = "bankReport" + selectedBankName + selectedDate; doc.save(documentName.toString().replace(/\s/g, "")); };

return ( <>

setAdditionalComments(e.target.value)} />
</>

); };

export default Pdf;

mmghv commented 7 months ago

Check out this example with a similar layout of what you want :

https://simonbengtsson.github.io/jsPDF-AutoTable/#multiple

code : https://github.com/simonbengtsson/jsPDF-AutoTable/blob/master/examples/examples.js#L135

thediveshsharma commented 7 months ago

Check out this example with a similar layout of what you want :

https://simonbengtsson.github.io/jsPDF-AutoTable/#multiple

code : https://github.com/simonbengtsson/jsPDF-AutoTable/blob/master/examples/examples.js#L135

We've got six units, and for each unit, we're generating tables for both deposits and withdrawals. The thing is, these tables can vary in length. Sometimes, one might be longer than the other.

To keep things neat, I'm trying to align these tables properly. My plan is to figure out which table (deposits or withdrawals) is longer, and then start the next table from where the longest one ends.

But here's the snag: when one of the tables stretches onto a new page, the coordinates go wonky. This messes up my calculations, especially when I'm trying to find the length of the longest table.

So, I'm looking for a way to handle this gracefully. I need to adjust the table positions dynamically, considering page breaks and making sure everything stays in place, regardless of how long the tables get.

Any ideas on how we can tackle this?

mmghv commented 6 months ago

I didn't review your code, but you would need to utilize pageNumber & finalY properties of the drawn tables to determine the longest one and draw the next table with startY accordingly.

thediveshsharma commented 6 months ago

I didn't review your code, but you would need to utilize pageNumber & finalY properties of the drawn tables to determine the longest one and draw the next table with startY accordingly.

thanks but I am struck on this since many days. I think this package owners dont reply to queries anymore. I will try some other package.

mmghv commented 6 months ago

I'm a collaborator in this project, you haven't shown an actual bug in the package, rather, you're unable to achieve a certain layout which is a question better suited for StackOverflow. our time is limited and we spend it trying to improve the package.

Closing this for now, please feel free to reopen if you discover an actual bug and are able to reproduce it in a codepen.

thediveshsharma commented 6 months ago

The bug is that if multi table layout height doesn't match and any table is so long that it spans multiple pages then there is no way to figure out which table is longer as y axis resets as soon as table moves to new page . The example given works because both tables have exact same rows/height. If possible can you give an example where there are 2 tables 1 below another which spans to different pages and all table has different heights?

On Mon, 15 Apr, 2024, 9:56 pm Mohamed Gharib, @.***> wrote:

I'm a collaborator in this project, you haven't shown an actual bug in the package, rather, you're unable to achieve a certain layout which is a question better suited for StackOverflow. our time is limited and we spend it trying to improve the package.

Closing this for now, please feel free to reopen if you discover an actual bug and are able to reproduce it in a codepen.

— Reply to this email directly, view it on GitHub https://github.com/simonbengtsson/jsPDF-AutoTable/issues/1050#issuecomment-2057263416, or unsubscribe https://github.com/notifications/unsubscribe-auth/APNZDAYKJV4MTZPSS7GZHNTY5P5UNAVCNFSM6AAAAABGDZ73A2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANJXGI3DGNBRGY . You are receiving this because you authored the thread.Message ID: @.***>

mmghv commented 6 months ago

I don't see any bugs there, you have pageNumber, finalY and startY which gives you the ability to figure out the pages spanned by the table, and the last Y position of the table on the last page, using this you could easily set the next table startY, if any of these don't work as expected or give a wrong value please feel free to report a specific bug, I might add an example demonstrating this when I have the time.

thediveshsharma commented 6 months ago

I don't know if this is a bug or not but suppose a there are 2 tables side by side. If any of them spans multiple pages . Table 1 :- starts from page1 and has startY = 120 And it ends on page 2 :- (startY resets) and it ends on page 2 and has Y as 120.

Table 2 :- starts from same , page1 and has startY = 120 It ends on page 1 on 200.

Now how can I figure out when to start my next multiple table ? Since the higher value is 200 but its on page 1 but actually the next table should start from 120 of next page (ignoring spaces between both tables) .

Can you help with this?

On Mon, 15 Apr, 2024, 10:14 pm Mohamed Gharib, @.***> wrote:

I don't see any bugs there, you have pageNumber, finalY and startY which gives you the ability to figure out the pages spanned by the table, and the last Y position of the table on the last page, using this you could easily set the next table startY, if any of these don't work as expected or give a wrong value please feel free to report a specific bug, I might add an example demonstrating this when I have the time.

— Reply to this email directly, view it on GitHub https://github.com/simonbengtsson/jsPDF-AutoTable/issues/1050#issuecomment-2057343912, or unsubscribe https://github.com/notifications/unsubscribe-auth/APNZDA5BSYC2DES64VCYQH3Y5P7WTAVCNFSM6AAAAABGDZ73A2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDANJXGM2DGOJRGI . You are receiving this because you authored the thread.Message ID: @.***>

mmghv commented 6 months ago

I would get pageNumber of both tables, table1:2 > table2:1 then I would get finalY of table1 (120) (because it spans more pages) and draw table3 giving startY using this.

Edit: don't forget to set the current doc page when drawing table3 to be the starting page + pageNumber of the longest table.

thediveshsharma commented 6 months ago

I would get pageNumber of both tables, table1:2 > table2:1 then I would get finalY of table1 (120) (because it spans more pages) and draw table3 giving startY using this.

Edit: don't forget to set the current doc page when drawing table3 to be the starting page + pageNumber of the longest table.

I used this and was able to achieve the desired layout. Thanks alot.. I was struck on this for 2 weeks. Finally decided to make a post and it helped me. <3 bankReportAxisBank2024-04-16.pdf I have attached a desired output file. Can I contribute and add an example of multitable with loop spanning multiple pages example? Or you can add it so others can benefit too?

mmghv commented 6 months ago

I'm glad to hear that. We are currently working on v4 which includes breaking changes so no use of adding new examples at the moment, it could be added after v4 is released.