Skip to main content

Adding Print Sales Journal and Export to Excel in Sells List

This guide will walk you through adding print and Excel export functionality to your sales journal in the sells list page.

Adding Print Sales Journal and Export to Excel in Sells List Adding Print Sales Journal and Export to Excel in Sells List "Print" Adding Print Sales Journal and Export to Excel in Sells List "Excel"

Step 1: Add the Sales Journal Dropdown Menu​

Navigate to your Laravel project and open the file:

resources/views/sell/index.blade.php

Locate the following code block:

<a class="tw-dw-btn tw-bg-gradient-to-r tw-from-indigo-600 tw-to-blue-500 tw-font-bold tw-text-white tw-border-none tw-rounded-full pull-right"
href="{{ action([\App\Http\Controllers\SellController::class, 'create']) }}">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="icon icon-tabler icons-tabler-outline icon-tabler-plus">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 5l0 14" />
<path d="M5 12l14 0" />
</svg> @lang('messages.add')
</a>

Add the following code BEFORE the above block:

<details id="sales-journal-dropdown" class="tw-dw-dropdown tw-relative tw-inline-block tw-text-left pull-right">
<summary
class="tw-dw-btn tw-bg-gradient-to-r tw-from-indigo-600 tw-to-blue-500 tw-font-bold tw-text-white tw-border-none tw-rounded-full m-2 pull-right">
<i class="fa fa-print"></i>
Sales journal
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="tw-size-5 tw-ml-1">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 15l-6-6h12l-6 6z"></path>
</svg>
</summary>

<ul class="tw-p-2 tw-w-48 tw-absolute tw-right-0 tw-z-10 tw-bg-white tw-rounded-lg tw-shadow-lg tw-ring-1 tw-ring-gray-200 tw-mt-0 focus:tw-outline-none"
role="menu" tabindex="-1" style="top: 100%; margin-top: 0.5rem;">
<li>
<a href="javascript:void(0);"
class="tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-2 tw-text-sm tw-font-medium tw-text-gray-600 tw-transition-all tw-duration-200 tw-rounded-lg hover:tw-text-gray-900 hover:tw-bg-gray-100"
role="menuitem" tabindex="-1" id="printSalesJournal">
<i class="fa fa-print"></i>
{{ __('messages.print') }}
</a>
</li>
<li>
<a href="javascript:void(0);"
class="tw-flex tw-items-center tw-gap-2 tw-px-3 tw-py-2 tw-text-sm tw-font-medium tw-text-gray-600 tw-transition-all tw-duration-200 tw-rounded-lg hover:tw-text-gray-900 hover:tw-bg-gray-100"
role="menuitem" tabindex="-1" id="exportSalesJournalToExcel">
<i class="fa fa-file-excel"></i>
{{ __('lang_v1.export_to_excel') }}
</a>
</li>
</ul>
</details>

Step 2: Add the JavaScript Functionality​

Locate the JavaScript section at the bottom of your Blade file. Find the @section('javascript') block:

@section('javascript')

@endsection

Add this javacript code before close @endsection:

<script type="text/javascript">
$(document).ready(function() {
// Content function
function getPrintContent() {
var table = $('#sell_table').DataTable();

// Get current date or selected date range
var dateRange = $('#sell_list_filter_date_range').val();
var formattedDate;

if (dateRange) {
formattedDate = dateRange;
} else {
var today = new Date();
var day = String(today.getDate()).padStart(2, '0');
var month = String(today.getMonth() + 1).padStart(2, '0');
var year = today.getFullYear();
formattedDate = `${day}/${month}/${year}`;
}

// Get selected location
var locationId = $('#sell_list_filter_location_id option:selected').text();
var locationText = locationId ? `Location: ${locationId}` : '';

// Create HTML content for printing
var printContent = `
<html>
<head>
<title>Sales Journal</title>
<style>
@page {
size: A4 landscape;
margin: 10mm 5mm;
}

body {
font-family: 'Segoe UI', Arial, sans-serif;
font-size: 10px;
margin: 0;
padding: 0;
color: #333;
background-color: white;
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}

.report-header {
text-align: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 2px solid #4a5568;
}

.company-name {
font-size: 16px;
font-weight: bold;
color: #2d3748;
margin: 0;
padding: 5px;
border-radius: 4px;
display: inline-block;
}

.report-title {
font-size: 14px;
font-weight: 600;
margin: 5px 0;
color: #4a5568;
}

.report-date {
font-size: 12px;
color: #4a5568;
margin-top: 5px;
}

table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
page-break-inside: auto;
}

thead {
display: table-header-group;
page-break-inside: avoid;
page-break-after: avoid;
}

tfoot {
display: table-footer-group;
page-break-inside: avoid;
}

tr {
page-break-inside: avoid;
}

th {
background-color: #edf2f7;
color: #2d3748;
font-size: 11px;
padding: 6px 4px;
text-align: center;
border: 1px solid #cbd5e0;
font-weight: 600;
}

td {
font-size: 10px;
padding: 4px;
border: 1px solid #e2e8f0;
text-align: left;
vertical-align: middle;
}

.text-right {
text-align: right;
}

.text-center {
text-align: center;
}

.section-header {
background-color: #edf2f7;
color: #2d3748;
font-weight: bold;
text-align: center;
padding: 6px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
}

.summary-row {
background-color: #f7fafc;
font-weight: bold;
}

.sub-header {
background-color: #f7fafc;
font-weight: 600;
text-align: center;
}

/* Zebra striping for data rows */
tbody tr:nth-child(even) {
background-color: #f8fafc;
}

tfoot tr:not(.section-header):not(.sub-header) {
background-color: #f9fafb;
}

@media print {
thead {
display: table-header-group;
}

tfoot {
display: table-footer-group;
}

button {
display: none;
}

body {
margin: 0;
}

.no-print {
display: none;
}
}
</style>
</head>
<body>
<div class="report-header">
<div class="company-name">${locationText}</div>
<div class="report-title">Sales Journal</div>
<div class="report-date">${formattedDate}</div>
</div>

<table>
<thead>
<tr>
<th width="30px">#</th>
<th style="white-space: nowrap;">Invoice No.</th>
<th>Amount</th>
<th>Total Paid</th>
<th>Payment Mode</th>
<th>Qty</th>
<th>Commission Agent</th>
<th>Customer</th>
<th>Customer Group</th>
</tr>
</thead>
<tbody>`;

// Variables for totals
var lineNumber = 1;
var totalPaidSum = 0;
var totalQteSum = 0;
var finalTotalSum = 0;
var totalDiscount = 0;

// Variables for grouping data
var paymentMethodsCount = {};
var paymentMethodsTotalPaid = {};
var paymentMethodsFinalTotal = {};

var commissionAgentsCount = {};
var commissionAgentsTotalPaid = {};
var commissionAgentsFinalTotal = {};

// added_by
var addedByCount = {};
var addedByTotalPaid = {};
var addedByFinalTotal = {};

var customerGroupCount = {};
var customerGroupTotalPaid = {};
var customerGroupFinalTotal = {};

var categoryByCount = {};
var categoryByTotalPaid = {};
var categoryByFinalTotal = {};

var locationByCount = {};
var locationByTotalPaid = {};
var locationByFinalTotal = {};

// Iterate through table rows
table.rows().every(function() {
var data = this.data();

// Clean and parse numeric values
var cleanedTotalPaid = parseFloat(data.total_paid.replace(/[^\d.,]/g, '').replace(',', '.')) || 0;
totalPaidSum += cleanedTotalPaid;

var cleanedFinalTotal = parseFloat(data.final_total.replace(/[^\d.,]/g, '').replace(',', '.')) || 0;
finalTotalSum += cleanedFinalTotal;

// Clean discount amount
var cleanedtotalDiscount = parseFloat(data.discount_amount?.replace(/[^\d.,]/g, '').replace(',', '.')) || 0;
totalDiscount += cleanedtotalDiscount;

// Add line discounts if available
if (data.sell_lines && data.sell_lines.length > 0) {
for (var i = 0; i < data.sell_lines.length; i++) {
var cleanedItem = parseFloat(data.sell_lines[i].line_discount_amount?.replace(/[^\d.,]/g, '').replace(',', '.')) || 0;
totalDiscount += cleanedItem;
}
}

// Payment method processing
var paymentMethod = data.payment_methods?.trim();
if (!paymentMethod) {
paymentMethod = 'CREDIT';
}

if (paymentMethodsCount[paymentMethod]) {
paymentMethodsCount[paymentMethod]++;
paymentMethodsTotalPaid[paymentMethod] += cleanedTotalPaid;
paymentMethodsFinalTotal[paymentMethod] += cleanedFinalTotal;
} else {
paymentMethodsCount[paymentMethod] = 1;
paymentMethodsTotalPaid[paymentMethod] = cleanedTotalPaid;
paymentMethodsFinalTotal[paymentMethod] = cleanedFinalTotal;
}

// Commission agents processing
var commissionAgent = data.commission_agent;
if (!commissionAgent || commissionAgent.trim() === '') {
commissionAgent = '--';
}

if (commissionAgentsCount[commissionAgent]) {
commissionAgentsCount[commissionAgent]++;
commissionAgentsTotalPaid[commissionAgent] += cleanedTotalPaid;
commissionAgentsFinalTotal[commissionAgent] += cleanedFinalTotal;
} else {
commissionAgentsCount[commissionAgent] = 1;
commissionAgentsTotalPaid[commissionAgent] = cleanedTotalPaid;
commissionAgentsFinalTotal[commissionAgent] = cleanedFinalTotal;
}

// added_by processing
var addedBy = data.added_by;
if (!addedBy || addedBy.trim() === '') {
addedBy = '--';
}

if (addedByCount[addedBy]) {
addedByCount[addedBy]++;
addedByTotalPaid[addedBy] += cleanedTotalPaid;
addedByFinalTotal[addedBy] += cleanedFinalTotal;
} else {
addedByCount[addedBy] = 1;
addedByTotalPaid[addedBy] = cleanedTotalPaid;
addedByFinalTotal[addedBy] = cleanedFinalTotal;
}



// Customer group processing
var customerGroup = data.customer_group;
if (!customerGroup || customerGroup.trim() === '') {
customerGroup = 'Default Selling Price';
}

if (customerGroupCount[customerGroup]) {
customerGroupCount[customerGroup]++;
customerGroupFinalTotal[customerGroup] += cleanedFinalTotal;
customerGroupTotalPaid[customerGroup] += cleanedTotalPaid;
} else {
customerGroupCount[customerGroup] = 1;
customerGroupFinalTotal[customerGroup] = cleanedFinalTotal;
customerGroupTotalPaid[customerGroup] = cleanedTotalPaid;
}

// Category processing
var category = '';
if (data.sell_lines && data.sell_lines.length > 0 &&
data.sell_lines[0].product && data.sell_lines[0].product.category) {
category = data.sell_lines[0].product.category.name;
}

if (!category || category.trim() === '') {
category = '--';
}

if (categoryByCount[category]) {
categoryByCount[category]++;
categoryByFinalTotal[category] += cleanedFinalTotal;
categoryByTotalPaid[category] += cleanedTotalPaid;
} else {
categoryByCount[category] = 1;
categoryByFinalTotal[category] = cleanedFinalTotal;
categoryByTotalPaid[category] = cleanedTotalPaid;
}

// Location processing
var location = data.business_location;
if (!location || location.trim() === '') {
location = '--';
}

if (locationByCount[location]) {
locationByCount[location]++;
locationByFinalTotal[location] += cleanedFinalTotal;
locationByTotalPaid[location] += cleanedTotalPaid;
} else {
locationByCount[location] = 1;
locationByFinalTotal[location] = cleanedFinalTotal;
locationByTotalPaid[location] = cleanedTotalPaid;
}

// Items count
var totalItems = parseFloat(data.total_items) || 0;
totalQteSum += totalItems;

// Customer name extraction
var htmlString = (data.name && data.name.trim() !== '') ? data.name : (data.supplier_business_name || '');
var customer;

if (htmlString.includes('<a')) {
var tempDiv = document.createElement('div');
tempDiv.innerHTML = htmlString;
var anchorTag = tempDiv.querySelector('a');
customer = anchorTag ? anchorTag.textContent.trim() : "--";
} else {
customer = htmlString.trim();
}

var customer_group = data.customer_group || 'Default Selling Price';
var commission_agent = data.commission_agent || '--';



// Format numbers for display
var formattedTotalPaid = cleanedTotalPaid.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedFinalTotal = cleanedFinalTotal.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

// Add row to table
printContent += `
<tr>
<td class="text-center">${lineNumber}</td>
<td style="white-space: nowrap;">${data.invoice_no}</td>
<td class="text-right">${formattedFinalTotal}</td>
<td class="text-right">${formattedTotalPaid}</td>
<td>${paymentMethod}</td>
<td class="text-right">${totalItems} P</td>
<td>${commission_agent}</td>
<td>${customer}</td>
<td>${customer_group}</td>
</tr>
`;
lineNumber++;
});

// Format totals
var formattedFooterTotalPaid = totalPaidSum.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedFooterFinalTotal = finalTotalSum.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedFooterTotalDiscount = totalDiscount.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

// Payment Methods Summary
var paymentMethodsFooter = '';
for (var method in paymentMethodsCount) {
var formattedTotalPaid = paymentMethodsTotalPaid[method].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedFinalTotal = paymentMethodsFinalTotal[method].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

paymentMethodsFooter += `
<tr>
<td colspan="4">${method}</td>
<td colspan="3" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-center">${paymentMethodsCount[method]}</td>
</tr>
`;
}

// Commission Agents Summary
var commissionAgentsFooter = '';
for (var agent in commissionAgentsCount) {
var formattedFinalTotal = commissionAgentsFinalTotal[agent].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedTotalPaid = commissionAgentsTotalPaid[agent].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

commissionAgentsFooter += `
<tr>
<td colspan="3">${agent}</td>
<td colspan="2" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-right">${formattedTotalPaid}</td>
<td colspan="2" class="text-center">${commissionAgentsCount[agent]}</td>
</tr>
`;
}

// added_by Summary
var addedByFooter = '';
for (var addedBy in addedByCount) {
var formattedFinalTotal = addedByFinalTotal[addedBy].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedTotalPaid = addedByTotalPaid[addedBy].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

addedByFooter += `
<tr>
<td colspan="3">${addedBy}</td>
<td colspan="2" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-right">${formattedTotalPaid}</td>
<td colspan="2" class="text-center">${addedByCount[addedBy]}</td>
</tr>
`;
}

// Customer Group Summary
var customerGroupFooter = '';
for (var group in customerGroupCount) {
var formattedFinalTotal = customerGroupFinalTotal[group].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedTotalPaid = customerGroupTotalPaid[group].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

customerGroupFooter += `
<tr>
<td colspan="3">${group}</td>
<td colspan="2" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-right">${formattedTotalPaid}</td>
<td colspan="2" class="text-center">${customerGroupCount[group]}</td>
</tr>
`;
}

// Product Categories Summary
var productCategoriesFooter = '';
for (var category in categoryByCount) {
var formattedFinalTotal = categoryByFinalTotal[category].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedTotalPaid = categoryByTotalPaid[category].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

productCategoriesFooter += `
<tr>
<td colspan="3">${category}</td>
<td colspan="2" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-right">${formattedTotalPaid}</td>
<td colspan="2" class="text-center">${categoryByCount[category]}</td>
</tr>
`;
}

// Locations Summary
var locationsFooter = '';
for (var location in locationByCount) {
var formattedFinalTotal = locationByFinalTotal[location].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

var formattedTotalPaid = locationByTotalPaid[location].toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});

locationsFooter += `
<tr>
<td colspan="3">${location}</td>
<td colspan="2" class="text-right">${formattedFinalTotal}</td>
<td colspan="2" class="text-right">${formattedTotalPaid}</td>
<td colspan="2" class="text-center">${locationByCount[location]}</td>
</tr>
`;
}

// Complete the HTML
printContent += `
</tbody>
<tfoot>
<tr class="summary-row">
<td colspan="4" class="text-center">
Total Amount: ${formattedFooterFinalTotal} / Total Discount: ${formattedFooterTotalDiscount}
</td>
<td colspan="3" class="text-center">Total Paid: ${formattedFooterTotalPaid}</td>
<td colspan="2" class="text-center">Total Qty: ${totalQteSum} Units</td>
</tr>

<tr><td colspan="9" class="section-header">DETAILS</td></tr>

<!-- Payment Mode Section -->
<tr><td colspan="9" class="section-header">A: PAYMENT MODE</td></tr>
<tr class="sub-header">
<td colspan="4">Mode</td>
<td colspan="3">Total Amount</td>
<td colspan="2">Count</td>
</tr>
${paymentMethodsFooter}

<!-- Customer Group Section -->
<tr><td colspan="9" class="section-header">B: CUSTOMER GROUP</td></tr>
<tr class="sub-header">
<td colspan="3">Customer</td>
<td colspan="2">Total Amount</td>
<td colspan="2">Total Paid</td>
<td colspan="2">Count</td>
</tr>
${customerGroupFooter}



<!-- Added by Section -->
<tr><td colspan="9" class="section-header">D: ADDED BY</td></tr>
<tr class="sub-header">
<td colspan="3">Added By</td>
<td colspan="2">Total Amount</td>
<td colspan="2">Total Paid</td>
<td colspan="2">Count</td>
</tr>
${addedByFooter}

<!-- Product Category Section -->
<tr><td colspan="9" class="section-header">D: PRODUCT CATEGORY</td></tr>
<tr class="sub-header">
<td colspan="3">Category</td>
<td colspan="2">Total Amount</td>
<td colspan="2">Total Paid</td>
<td colspan="2">Count</td>
</tr>
${productCategoriesFooter}

<!-- Location Section -->
<tr><td colspan="9" class="section-header">E: LOCATION</td></tr>
<tr class="sub-header">
<td colspan="3">Location</td>
<td colspan="2">Total Amount</td>
<td colspan="2">Total Paid</td>
<td colspan="2">Count</td>
</tr>
${locationsFooter}
</tfoot>
</table>
</body>
</html>
`;

return printContent;
}

// Print button handler
$('#printSalesJournal').on('click', function() {
var printContent = getPrintContent();
var printWindow = window.open('', '', 'height=800,width=1000');
printWindow.document.open();
printWindow.document.write(printContent);
printWindow.document.close();

// Add a slight delay to ensure content is loaded
setTimeout(function() {
printWindow.focus();
printWindow.print();
}, 250);
});

// Export to Excel button handler
$('#exportSalesJournalToExcel').on('click', function() {
exportToExcel('sales-journal-export');
});

function exportToExcel(filename = 'sales-journal-export') {
// Create a temporary element to hold the HTML
var tempDiv = document.createElement('div');
tempDiv.innerHTML = getPrintContent();

// Get the table element from the temporary div
var table = tempDiv.querySelector('table');

// Add inline CSS for borders to the table and cells
table.style.borderCollapse = 'collapse';
table.style.width = '100%';

const rows = table.querySelectorAll('tr');
rows.forEach(row => {
row.style.border = '1px solid black';
const cells = row.querySelectorAll('td, th');
cells.forEach(cell => {
cell.style.border = '1px solid black';
cell.style.padding = '5px';
});
});

// Create a new workbook and worksheet
var workbook = XLSX.utils.book_new();
var worksheet = XLSX.utils.table_to_sheet(table);

// Set column widths (approximate values)
var wscols = [
{wch: 5}, // #
{wch: 15}, // Invoice No
{wch: 12}, // Amount
{wch: 12}, // Total Paid
{wch: 15}, // Payment Mode
{wch: 8}, // Qty
{wch: 20}, // Commission Agent
{wch: 20}, // Customer
{wch: 20} // Customer Group
];

worksheet['!cols'] = wscols;

// Append the worksheet to the workbook
XLSX.utils.book_append_sheet(workbook, worksheet, 'SalesJournal');

// Export the workbook to a .xlsx file
XLSX.writeFile(workbook, filename + ".xlsx");
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>

💛 Support this project

Premium Login