Untitled

 avatar
unknown
plain_text
18 days ago
9.4 kB
5
Indexable
public class ExcelUtility
{
    // ... (keep all existing code) ...

    public byte[] ExportToExcel(IEnumerable<BookingOrderVM> orders, IEnumerable<TruckType> truckTypes, string uom)
    {
        using (var workbook = new XLWorkbook())
        {
            var worksheet = workbook.Worksheets.Add("Booking Portal");
            var currentRow = 1;
            var dropdownSheet = workbook.Worksheets.Add("Dropdowns");
            CreateDropdownLists(dropdownSheet, truckTypes);

            // Add a hidden helper sheet for tracking row configurations
            var helperSheet = workbook.Worksheets.Add("RowConfig");
            helperSheet.Hide();

            AddHeaders(worksheet, ref currentRow, true, uom);
            worksheet.Row(1).Style.Protection.SetLocked(true);

            foreach (var order in orders)
            {
                var orderStartRow = currentRow;
                AddDispatchRow(worksheet, ref currentRow, order, uom);
                worksheet.Row(orderStartRow).Style.Protection.SetLocked(true);

                // Store order start row in helper sheet
                helperSheet.Cell(orderStartRow, 1).Value = "OrderStart";

                AddHeaders(worksheet, ref currentRow, false, uom);
                worksheet.Row(currentRow - 1).Style.Protection.SetLocked(true);

                foreach (var booking in order.Bookings)
                {
                    AddBookingRow(worksheet, ref currentRow, booking, uom, orderStartRow);
                    // Mark booking rows in helper sheet
                    helperSheet.Cell(currentRow - 1, 1).Value = "Booking";
                    helperSheet.Cell(currentRow - 1, 2).Value = orderStartRow;
                }

                for (int i = 0; i < 3; i++)
                {
                    AddBookingRow(worksheet, ref currentRow, new Booking(), uom, orderStartRow);
                    // Mark empty booking rows in helper sheet
                    helperSheet.Cell(currentRow - 1, 1).Value = "Booking";
                    helperSheet.Cell(currentRow - 1, 2).Value = orderStartRow;
                }

                // Add the button row before remaining row
                AddButtonRow(worksheet, ref currentRow, orderStartRow);
                
                AddRemainingRow(worksheet, ref currentRow, orderStartRow, uom);
                worksheet.Row(currentRow - 1).Style.Protection.SetLocked(true);

                // Mark remaining row in helper sheet
                helperSheet.Cell(currentRow - 1, 1).Value = "Remaining";
                helperSheet.Cell(currentRow - 1, 2).Value = orderStartRow;

                currentRow++;
            }

            // Add worksheet change event handling formula
            AddWorksheetChangeHandler(worksheet, helperSheet);

            AddDropdowns(worksheet, truckTypes);
            worksheet.Columns().AdjustToContents();
            
            // Modify protection to allow using formulas
            worksheet.Protect()
                .AllowElement(XLSheetProtectionElements.SelectLockedCells)
                .AllowElement(XLSheetProtectionElements.SelectUnlockedCells)
                .AllowElement(XLSheetProtectionElements.FormatCells)
                .AllowElement(XLSheetProtectionElements.InsertRows)
                .AllowElement(XLSheetProtectionElements.DeleteRows)
                .AllowElement(XLSheetProtectionElements.UseAutoFilter)
                .AllowElement(XLSheetProtectionElements.Sort)
                .AllowElement(XLSheetProtectionElements.UsePivotTables);

            using (var stream = new MemoryStream())
            {
                workbook.SaveAs(stream);
                return stream.ToArray();
            }
        }
    }

    private void AddButtonRow(IXLWorksheet worksheet, ref int currentRow, int orderStartRow)
    {
        var addButtonCell = worksheet.Cell(currentRow, 1);
        addButtonCell.Value = "➕ Add Row";
        addButtonCell.Style.Fill.BackgroundColor = XLColor.FromArgb(200, 230, 201);
        addButtonCell.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
        addButtonCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
        addButtonCell.Style.Font.Bold = true;

        var comment = addButtonCell.CreateComment();
        comment.AddText("To add a new row:");
        comment.AddNewLine();
        comment.AddText("1. Select this row");
        comment.AddNewLine();
        comment.AddText("2. Right-click → Insert → Copy formulas from above");
        
        worksheet.Range(currentRow, 1, currentRow, 3).Merge();
        
        var deleteInstructionCell = worksheet.Cell(currentRow, 4);
        deleteInstructionCell.Value = "✖ Delete: Select row → Right-click → Delete";
        deleteInstructionCell.Style.Fill.BackgroundColor = XLColor.FromArgb(255, 205, 210);
        deleteInstructionCell.Style.Border.OutsideBorder = XLBorderStyleValues.Thin;
        deleteInstructionCell.Style.Alignment.Horizontal = XLAlignmentHorizontalValues.Center;
        deleteInstructionCell.Style.Font.Bold = true;
        
        worksheet.Range(currentRow, 4, currentRow, 7).Merge();
        
        currentRow++;
    }

    private void AddWorksheetChangeHandler(IXLWorksheet worksheet, IXLWorksheet helperSheet)
    {
        // Add a named range for the entire used range
        var usedRange = worksheet.RangeUsed();
        worksheet.Workbook.NamedRanges.Add("UsedRange", usedRange);

        // Add a worksheet-level formula to detect changes
        var formulaCell = worksheet.Cell(1, worksheet.ColumnCount() + 1);
        formulaCell.FormulaA1 = "=IF(ROW()>1,INDIRECT(\"RC[-1]\",FALSE),\"\")";
        formulaCell.Style.Protection.SetLocked(true);
        
        // Add helper formulas for row operations
        var helperFormula = helperSheet.Cell(1, 3);
        helperFormula.FormulaA1 = "=COUNTA(UsedRange)";
        
        // Add auto-configuration formula for new rows
        for (int row = 2; row <= usedRange.RowCount(); row++)
        {
            var configCell = helperSheet.Cell(row, 3);
            configCell.FormulaA1 = $"=IF(AND(ROW()>1,INDIRECT(\"A\"&ROW())=\"Booking\"),ConfigureNewRow(ROW()),\"\")";
        }
    }

    // Helper function to configure new rows (called by Excel formulas)
    private string ConfigureNewRow(int row)
    {
        var worksheet = (IXLWorksheet)Workbook.Worksheet("Booking Portal");
        var helperSheet = (IXLWorksheet)Workbook.Worksheet("RowConfig");
        
        // Get order start row from helper sheet
        var orderStartRow = int.Parse(helperSheet.Cell(row, 2).Value.ToString());
        
        // Copy validations and formulas from row above
        CopyRowConfiguration(worksheet, row - 1, row, orderStartRow);
        
        return "Configured";
    }

    private void CopyRowConfiguration(IXLWorksheet worksheet, int sourceRow, int targetRow, int orderStartRow)
    {
        // Copy data validation
        CopyDataValidation(worksheet.Row(sourceRow), worksheet.Row(targetRow));
        
        // Copy formulas and formatting
        CopyFormulasAndFormatting(worksheet.Row(sourceRow), worksheet.Row(targetRow));
        
        // Update remaining calculations
        UpdateRemainingCalculations(worksheet, targetRow, orderStartRow);
    }

    private void CopyDataValidation(IXLRow sourceRow, IXLRow targetRow)
    {
        // Trip dropdown
        var tripValidation = targetRow.Cell(3).SetDataValidation();
        tripValidation.List("=TripNumbers");
        
        // Truck type dropdown
        var truckTypeValidation = targetRow.Cell(4).SetDataValidation();
        truckTypeValidation.List("=TruckTypes");
        
        // Time picker
        var timeValidation = targetRow.Cell(5).SetDataValidation();
        timeValidation.List("=TimePicker");
        targetRow.Cell(5).Style.NumberFormat.Format = "HH:mm";
    }

    private void CopyFormulasAndFormatting(IXLRow sourceRow, IXLRow targetRow)
    {
        for (int col = 1; col <= sourceRow.Worksheet.ColumnCount(); col++)
        {
            var sourceCell = sourceRow.Cell(col);
            var targetCell = targetRow.Cell(col);
            
            if (sourceCell.HasFormula)
            {
                targetCell.FormulaA1 = sourceCell.FormulaA1;
            }
            
            targetCell.Style = sourceCell.Style;
        }
    }

    private void UpdateRemainingCalculations(IXLWorksheet worksheet, int row, int orderStartRow)
    {
        // Update the remaining row calculations
        var remainingRow = FindRemainingRow(worksheet, orderStartRow);
        if (remainingRow > 0)
        {
            UpdateRemainingRowFormulas(worksheet, remainingRow, orderStartRow);
        }
    }

    private int FindRemainingRow(IXLWorksheet worksheet, int orderStartRow)
    {
        var currentRow = orderStartRow + 1;
        while (!worksheet.Row(currentRow).IsEmpty())
        {
            if (worksheet.Cell(currentRow, 1).Value.ToString() == "Remaining")
            {
                return currentRow;
            }
            currentRow++;
        }
        return -1;
    }

    // ... (keep rest of existing code) ...
}
Leave a Comment