Files
advertising-profit-control/AdvertsingProfitControl/NewModifyRecord.cs
T

4773 lines
272 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Entity.Infrastructure;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Transactions;
using System.Windows.Forms;
using AdvertisingProfitControlData;
using DataTableParsingEngine;
namespace AdvertsingProfitControl
{
public partial class NewModifyRecord : Form
{
//TODO: Re-enable drag and drop on the modify record form.
//http://stackoverflow.com/questions/6219454/efficient-way-to-remove-all-whitespace-from-string
private static readonly FrmLogConsole LogConsole = FrmLogConsole.GetStaticInstance;
//Variables used to allow drag and drop functionality.
private Rectangle _dragBoxFromMouseDown;
private int _rowIndexFromMouseDown;
private int _rowIndexOfItemUnderMouseToDrop;
//Create an array that contains all the ad items from the database.
private List<string> _adItemCollection;
//This object contains all the unused ad items.
private AutoCompleteStringCollection _trimmedAdItemCollection = new AutoCompleteStringCollection();
//Contains all the ad specials that are in the database (i.e. "Daily Coupons").
private readonly AutoCompleteStringCollection _adSpecialList = new AutoCompleteStringCollection();
//This object contains all the suppliers that were found in the database.
private readonly AutoCompleteStringCollection _supplierCollection = new AutoCompleteStringCollection();
//This array keeps track of the number of times an Ad Item is used, if used once then it may only be used AFTER an AdSpecialRow and only once after that.
//Once an item has been used twice, it will not appear again in the AutoCompleteSuggestions.
//The structure of the used ad item list is as follows:
//List(0) is section one and List(1) is section two. Section one is not ad special and section two is.
private readonly List<string>[] _usedAdItems = new List<string>[2];
//This string keeps track of the last ad item used. This item can then be used to safely remove an ad item from the list of used items.
//Used in the OnCellValidating event to store the last ad item used in the event that the user changes a row that already exists.
private string _beginningCellValue = string.Empty;
//Flag showing whether or not the AdSpecialRow has been made in this session.
private int _adSpecialIndex = -1;
//Represents the date that is being modified, if this changes then the form needs to be cleared and refilled with the new date.
private DateTime _currentActiveDate;
//
private bool _isFormDirty;
//
private bool _isModifyingRecord;
private int _selectedRowIndex = -1;
public NewModifyRecord()
{
InitializeComponent();
var db = new AdvertisingProfitControlDbContext();
//Check to see if there are dates in the database.
if (db.WeekEndingDates.Any())
{
//The database is not empty, so grab the first value from descending.
var date = db.WeekEndingDates.OrderByDescending(x => x.EndingDate).FirstOrDefault();
if (date != null)
SetNewRecordDate(date.EndingDate, 1);
}
else
{
SetNewRecordDate(DateTime.Today);
}
_currentActiveDate = weekEndingCalendar.SelectionStart;
InitializeForm();
//_debugTabPage = mainTabControl.TabPages[4];
mainTabControl.TabPages.Remove(mainTabControl.TabPages[4]);
Text = @"Add New Record (Current Record: " + _currentActiveDate.ToShortDateString() + @")";
_isModifyingRecord = false;
addRecordButton.Text = @"Add Record";
}
public NewModifyRecord(DateTime date)
{
InitializeComponent();
//
weekEndingCalendar.SelectionStart = date;
_currentActiveDate = date;
InitializeForm();
mainTabControl.TabPages.Remove(mainTabControl.TabPages[4]);
Text = @"Modify Record (Current Record: " + date.ToShortDateString() + @")";
LoadDate(date);
}
public sealed override string Text
{
get { return base.Text; }
set { base.Text = value; }
}
#region Global Text Box Events
/// <summary>
/// Stores the beginning value in a text box into the _beginningCellValue field.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StoreBeginningTextBoxValue(object sender, EventArgs e)
{
var textBox = (TextBox)sender;
_beginningCellValue = textBox.Text.Trim();
}
#endregion
#region Invoice Table Events
/// <summary>
/// Event Used: RowValidating
/// Validates that the row has required information, namely an invoice date, number and supplier.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void ValidateInvoiceRow(object sender, DataGridViewCellCancelEventArgs e)
{
var dataGridView = (DataGridView)sender;
if (dataGridView.Rows[e.RowIndex].IsNewRow) return;
DateTime dateTime;
//Check to make sure the Invoice Date, Invoice Number and the Supplier values are set.
if (dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString().Length == 0 || !DateTime.TryParse(dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString(), out dateTime))
{
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"An invoice date must be specified.", @"Invalid Invoice Date", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceDate];
return;
}
if (dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.Supplier].EditedFormattedValue.ToString().Trim().Length == 0)
{
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"A supplier must be specified.", @"Invalid Supplier", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.Supplier];
return;
}
if (
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNumber].EditedFormattedValue
.ToString().Trim().Length == 0)
{
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"An invoice number must be specified.", @"Invalid Invoice Number", MessageBoxButtons.OK,
MessageBoxIcon.Error);
dataGridView.CurrentCell = dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNumber];
e.Cancel = true;
}
}
/// <summary>
/// Event Used: CellValidating
/// Verifies the contents of the invoice table's cells. Also applies formating where needed.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateInvoicesCellContents(object sender, DataGridViewCellValidatingEventArgs e)
{
//Grab the DataGirdView that fired the event and make it into a local variable.
var dataGridView = (DataGridView)sender;
var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString().Trim();
var textInfo = new CultureInfo("en-US", false).TextInfo;
//Check for isNewRow if it is, return no need to check it for anything.
if (dataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
//Mark the row as dirty assuming the user made changes
if (userInput != _beginningCellValue)
{
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.IsDirty].Value = true;
}
else
{
return;
}
//Check to see if we're in the invoice date column make sure the date is valid.
switch (e.ColumnIndex)
{
case (int)TableGroupParser.InvoiceTableColumns.InvoiceDate:
//Clear any error text a cell has for this column.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
if (userInput != string.Empty)
{
DateTime date;
//Try parsing the date to make sure its valid, otherwise clear it from the cell and inform the user.
if (DateTime.TryParse(userInput, out date))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = date.ToString("d");
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
MessageBox.Show(@"The date '" + userInput + @"' is not a valid date.", @"Invalid Date", MessageBoxButtons.OK, MessageBoxIcon.Error);
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Invoice Date Must be in a Valid Format";
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
}
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "An Invoice Date is Required";
}
break;
case (int)TableGroupParser.InvoiceTableColumns.InvoiceNumber:
//Clear any error text a cell has for this column.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
if (userInput != string.Empty)
{
long parsedNumber;
if (long.TryParse(userInput, out parsedNumber))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = parsedNumber;
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
MessageBox.Show(@"The invoice number must be a numeric value.", @"Non Numeric Invoice Number", MessageBoxButtons.OK, MessageBoxIcon.Error);
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Invoice Number Must be Numeric";
}
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "An Invoice Number is Required";
}
break;
case (int)TableGroupParser.InvoiceTableColumns.Supplier:
//Clear any error text a cell has for this column.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
if (userInput != string.Empty)
{
//TODO: Create a custom engine to apply formating to a supplier's name.
//Pretty up the entered text since there is something here.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = textInfo.ToTitleCase(userInput);
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "A Supplier is Required";
}
break;
case (int)TableGroupParser.InvoiceTableColumns.InvoiceNote:
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
break;
default:
//Should only process the InvoiceNetAmountAtCost and InvoiceNetAmount columns.
if (e.ColumnIndex != (int)TableGroupParser.InvoiceTableColumns.Id &&
e.ColumnIndex < (int)TableGroupParser.InvoiceTableColumns.InvoiceNote)
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
//Now check to make sure the user input isn't null
//Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
if (userInput != string.Empty)
{
double parsedNumber;
if (double.TryParse(userInput, out parsedNumber))
{
//Since the input is a number format it to show the cents and display it.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "Only Numeric Values Allowed.";
}
}
}
break;
}
dataGridView.RefreshEdit();
}
/// <summary>
/// Event Used: EditingShadowControlShowing
/// Adds an auto-complete list of suppliers to the Suppliers cell.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DisplaySupplierAutoComleteOnEditingShadowControl(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var dataGridView = (DataGridView)sender;
var autoText = e.Control as TextBox;
if (autoText == null) return;
if (!(e.Control is DataGridViewTextBoxEditingControl) || dataGridView.CurrentCell.ColumnIndex != (int)TableGroupParser.InvoiceTableColumns.Supplier) return;
autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
autoText.AutoCompleteCustomSource = _supplierCollection;
}
/// <summary>
/// Checks to see if the row that the user is attempting to delete has been saved to the database.
/// If so, this method will try to delete the record, failing that it will cancel the row deletion.
/// </summary>
/// <param name="sender">Invoice table.</param>
/// <param name="e"></param>
private void UpdateInvoicesOnRowDeleting(object sender, DataGridViewRowCancelEventArgs e)
{
var rowIndex = e.Row.Index;
//See if there is an ID number in the ID column.
if (invoicesDataGridView.Rows[rowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue.ToString() == string.Empty) return;
//Ask the user to make damn sure they want to remove this record.
var result = MessageBox.Show(@"Deleting this row will remove it from the database permanently. Do you wish to continue?", @"Remove Invoice Number " + invoicesDataGridView.Rows[rowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNumber].EditedFormattedValue, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
//If so create the database interaction objects.
var id =
int.Parse(
invoicesDataGridView.Rows[rowIndex].Cells[(int)TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue
.ToString());
if (DeleteInvoiceRow(id))
{
informationLabel.Text = @"Successfully removed row " + (rowIndex + 1) + @" from the database.";
}
else
{
//The operation failed.
errorLabel.Text = @"Failed to delete row " + (rowIndex + 1) + @" from invoices.";
e.Cancel = true;
}
}
else
{
e.Cancel = true;
}
}
private void ToggleInvoicesEvents(bool enable = true)
{
if (enable)
{
invoicesDataGridView.CellEnter += StoreBeginningCellValue;
invoicesDataGridView.RowValidating += ValidateInvoiceRow;
invoicesDataGridView.CellValidating += ValidateInvoicesCellContents;
invoicesDataGridView.EditingControlShowing += DisplaySupplierAutoComleteOnEditingShadowControl;
invoicesDataGridView.UserDeletingRow += UpdateInvoicesOnRowDeleting;
}
else
{
invoicesDataGridView.CellEnter -= StoreBeginningCellValue;
invoicesDataGridView.RowValidating -= ValidateInvoiceRow;
invoicesDataGridView.CellValidating -= ValidateInvoicesCellContents;
invoicesDataGridView.EditingControlShowing -= DisplaySupplierAutoComleteOnEditingShadowControl;
invoicesDataGridView.UserDeletingRow -= UpdateInvoicesOnRowDeleting;
}
}
#endregion
#region APC DataGridView Events
/// <summary>
/// Event Used: RowsAdded
/// Draws the row number in the row's cell header whenever a row is added.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void DisplayRowNumbers(object sender, DataGridViewRowsAddedEventArgs e)
{
var table = ((DataGridView)sender);
table.Rows[e.RowIndex].HeaderCell.Value = (e.RowIndex + 1).ToString();
}
/// <summary>
/// Event Used: CellEnter
/// Stores the initial contents of the cell being entered to be compared later
/// to see if the user has made any changes (IsDirty).
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StoreBeginningCellValue(object sender, DataGridViewCellEventArgs e)
{
var dataGridView = (DataGridView)sender;
_beginningCellValue = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
}
/// <summary>
/// Keeps track of the currently selected row index in the APC DataGridView that has focus.
/// This is used by the key down event handler in the cell's text box to determine what auto-complete
/// list to use.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpdateSelectedRowIndexOnEnter(object sender, DataGridViewCellEventArgs e)
{
_selectedRowIndex = e.RowIndex;
}
/// <summary>
/// Event Used: OnRowLeave
/// Adds the ad items to the used ad item collection if they are not already in the collection.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UpdateUsedAdItemCollectionOnRowLeave(object sender, DataGridViewCellEventArgs e)
{
var dataGridView = ((DataGridView)sender);
if (dataGridView.Rows[e.RowIndex].IsNewRow) { return; } //Return if the row is a new row as nothing needs to be done here.
int adItemIndex;
switch (dataGridView.Name)
{
case "projectionsDataGridView":
adItemIndex = (int)TableGroupParser.SalesTableColumns.AdItem;
break;
case "actualSalesDataGridView":
adItemIndex = (int)TableGroupParser.SalesTableColumns.AdItem;
break;
default:
adItemIndex = (int)TableGroupParser.InventoryTableColumns.AdItem;
break;
}
var userInput = dataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString().Trim();
//Add in used Ad Items to the list.
if (userInput == string.Empty) return;
//Clear the old ad item out of the used ad item collection.
//Section one (1) detected.
if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
{
if (_usedAdItems[0].Contains(userInput)) return;
_usedAdItems[0].Add(userInput);
}
//Section two (2) detected.
else if(e.RowIndex > _adSpecialIndex)
{
if (_usedAdItems[1].Contains(userInput)) return;
_usedAdItems[1].Add(userInput);
}
}
/// <summary>
/// Event Used: OnEditingControlShowing
/// Configures the auto complete collection and how it will be shown to the user. This method detects the section,
/// either one (1) or two (2), based on the gAdSpecialIndex and removes items from the auto complete accordingly.
/// Just a measure to help reduce redundancy in the tables.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DisplayAutoCompleteOnEditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
var dataGridView = ((DataGridView)sender);
var autoText = e.Control as TextBox;
//Get the index of the ad item column
const int adItemIndex = (int)TableGroupParser.SalesTableColumns.AdItem;
if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex == adItemIndex)
{
//Create a copy of the main ad item list that can be freely manipulated.
var customAutoComplete = new AutoCompleteStringCollection();
var customList = _adItemCollection.ToList();
//IF the current row is less than the AdSpecialRow, remove all used items with the section 1 attribute.
if (_adSpecialIndex == -1 || dataGridView.CurrentCell.RowIndex < _adSpecialIndex)
{
foreach (var adItem in _usedAdItems[0])
{
customList.RemoveAll(w => w.Equals(adItem, StringComparison.OrdinalIgnoreCase));
}
}
//Occurs AFTER the AdSpecial row.
else if (dataGridView.CurrentCell.RowIndex > _adSpecialIndex)
{
foreach (var adItem in _usedAdItems[1])
{
customList.RemoveAll(w => w.Equals(adItem, StringComparison.OrdinalIgnoreCase));
}
}
foreach (var item in customList)
{
customAutoComplete.Add(item);
}
autoText.KeyDown += ChangeAutoCompleteListOnKeyCombo;
_trimmedAdItemCollection = customAutoComplete; //Make a temporary copy of the list for use with the TextBox event handler.
autoText.AutoCompleteMode = AutoCompleteMode.Suggest;
autoText.AutoCompleteSource = AutoCompleteSource.CustomSource;
autoText.AutoCompleteCustomSource = customAutoComplete;
}
else if (e.Control is DataGridViewTextBoxEditingControl && dataGridView.CurrentCell.ColumnIndex != adItemIndex)
{
autoText.AutoCompleteMode = AutoCompleteMode.None;
}
}
/// <summary>
/// Event Used: UserDeleteingRow
/// This function is responsible for removing ad Items from the gUsedAdItem array; this must be done during the row removing
/// event handler so the data in the row can be grabbed and used.
/// </summary>
/// <param name="sender">The DataGridView that fired the event.</param>
/// <param name="e">Parameters, mainly allowing for canceling the event.</param>
private void UpdateUsedAdItemCollectionOnRowRemoving(object sender, CancelEventArgs e)
{
//Create an object that represents the DataGridView that fired the event.
var dataGridView = (DataGridView)sender;
if (dataGridView.CurrentRow == null) return;
//Create the database writer object so rows that are in the database can be deleted.
var currentRowIndex = dataGridView.CurrentRow.Index;
//Remove the ad item from the gUsedAdItem collection, if it exists.
if (_adSpecialIndex == -1)
{
//If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
{
var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
//Attempt to delete the row from the database by its ID number.
//Attempt to delete the row from the database by its ID number.
int.TryParse(
projectionsDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id]
.EditedFormattedValue.ToString(), out int projectionsRowId);
int.TryParse(
inventoryDataGridView.Rows[currentRowIndex].Cells[(int) TableGroupParser.SalesTableColumns.Id]
.EditedFormattedValue.ToString(), out int inventoryRowId);
int.TryParse(
actualSalesDataGridView.Rows[currentRowIndex].Cells[(int) TableGroupParser.SalesTableColumns.Id]
.EditedFormattedValue.ToString(), out int actualSalesRowId);
if (DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
{
informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) +
@" from the database.";
}
else
{
//If the removing failed cancel the row deletion in the DataGridView.
e.Cancel = true;
MessageBox.Show(
@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
@" from the database.", @"Failed to Update Database");
return;
}
}
else
{
e.Cancel = true;
return;
}
}
//If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
_usedAdItems[0].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString());
FlagRowsAsDirty(dataGridView, currentRowIndex);
}
else if (currentRowIndex < _adSpecialIndex)
{
//If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
{
var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
//Attempt to delete the row from the database by its ID number.
//Attempt to delete the row from the database by its ID number.
int.TryParse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int projectionsRowId);
int.TryParse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int inventoryRowId);
int.TryParse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int actualSalesRowId);
if (DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
{
informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
}
else
{
//If the removing failed cancel the row deletion in the DataGridView.
e.Cancel = true;
MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
@" from the database.", @"Failed to Update Database");
return;
}
}
else
{
e.Cancel = true;
return;
}
}
//If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
_usedAdItems[0].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString());
//Mark the rows as dirty before decrementing the _adSpecialIndex since this wouldn't account for the fact that the current row hasn't been cleared yet.
FlagRowsAsDirty(dataGridView, currentRowIndex, _adSpecialIndex);
//Also decrement the _adSpecialIndex so that it points to the correct row.
_adSpecialIndex--;
}
else if (currentRowIndex > _adSpecialIndex)
{
//If the ID number is set, i.e. not equal to null then attempt to remove it from the database.
if (dataGridView.Rows[currentRowIndex].Cells[0].EditedFormattedValue.ToString() != string.Empty)
{
var result = MessageBox.Show(@"Removing this row will permanently delete this record from the database. Do you wish to continue?", @"Remove " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo);
if (result == DialogResult.Yes)
{
//Attempt to delete the row from the database by its ID number.
var projectionsRowId = int.Parse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString());
var inventoryRowId = int.Parse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString());
var actualSalesRowId = int.Parse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString());
if (DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
{
informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
}
else
{
//If the removing failed cancel the row deletion in the DataGridView.
e.Cancel = true;
MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
@" from the database.", @"Failed to Update Database");
return;
}
}
else
{
e.Cancel = true;
return;
}
}
//If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
_usedAdItems[1].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString());
FlagRowsAsDirty(dataGridView, currentRowIndex);
}
else if (currentRowIndex == _adSpecialIndex)
{
//Handle removing the AdSpecial row.
var result = MessageBox.Show(@"Deleting the Ad Special row will remove all rows beneath it. Do you wish to continue?", @"Clear " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
//Clear all events that handle row removal from both DataGridViews.
//Projections table
projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
//Inventory table
inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
//Actual Sales table
actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
//Now for-each through each row that is underneath the Ad Special row.
for (var rowIndex = dataGridView.RowCount; currentRowIndex != rowIndex; rowIndex--)
{
if (projectionsDataGridView.RowCount == inventoryDataGridView.RowCount &&
projectionsDataGridView.RowCount == actualSalesDataGridView.RowCount)
{
if (dataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
//Attempt to delete the row from the database by its ID number.
int.TryParse(projectionsDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int projectionsRowId);
int.TryParse(inventoryDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int inventoryRowId);
int.TryParse(actualSalesDataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString(), out int actualSalesRowId);
if (DeleteApcRow(projectionsRowId, inventoryRowId, actualSalesRowId))
{
informationLabel.Text = @"Successfully removed row " + (currentRowIndex + 1) + @" from the database.";
}
else
{
//If the removing failed cancel the row deletion in the DataGridView.
e.Cancel = true;
MessageBox.Show(@"Failed to delete " + dataGridView.Rows[currentRowIndex].Cells[1].EditedFormattedValue +
@" from the database.", @"Failed to Update Database");
return;
}
}
}
if (projectionsDataGridView.Rows[currentRowIndex].IsNewRow != true)
{
projectionsDataGridView.Rows.RemoveAt(currentRowIndex);
}
if (inventoryDataGridView.Rows[currentRowIndex].IsNewRow != true)
{
inventoryDataGridView.Rows.RemoveAt(currentRowIndex);
}
if (actualSalesDataGridView.Rows[currentRowIndex].IsNewRow != true)
{
actualSalesDataGridView.Rows.RemoveAt(currentRowIndex);
}
//If the ad item entered in the first cell is in the gUsedAdItems collection, then remove it from there.
_usedAdItems[1].Remove(dataGridView.Rows[currentRowIndex].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString());
}
//Re-enable all row removal events on both tables.
//Projections table
projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
//Inventory table
inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
//Actual Sales table
actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
//Reset the gAdSpecialIndex to -1.
_adSpecialIndex = -1;
e.Cancel = true; //Prevent the new row from being removed.
return;
}
e.Cancel = true;
}
}
private void FlagRowsAsDirty(DataGridView dataGridView, int currentRowIndex, int adSpecialIndex = -1)
{
//Check to see if the user started to add a row but then decided against it, does not mark the form dirty.
if (dataGridView.Rows.Count != inventoryDataGridView.Rows.Count ||
dataGridView.Rows.Count != actualSalesDataGridView.Rows.Count) return;
//Mark all rows under the row that just got deleted as dirty.
_isFormDirty = true;
for (var index = currentRowIndex; index < dataGridView.Rows.Count; index++)
{
//Do not color code the NewRow just break, we're done.
if (dataGridView.Rows[index].IsNewRow) break;
//Do not color code the Ad Special Row, just for preference.
if (index == adSpecialIndex)
{
//Clear it's row color coding just in-case the user clears more than one row.
projectionsDataGridView.Rows[index].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = false;
projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
inventoryDataGridView.Rows[index].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = false;
inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
actualSalesDataGridView.Rows[index].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = false;
actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor = DefaultBackColor;
continue;
}
projectionsDataGridView.Rows[index].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor = TableColors.PendingEdit;
inventoryDataGridView.Rows[index].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[index].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
}
private void ChangeAutoCompleteListOnKeyCombo(object sender, KeyEventArgs e)
{
var textBox = (TextBox)sender;
informationLabel.Text = string.Empty;
if (e.Modifiers == Keys.Alt && e.KeyCode == Keys.S)
{
if (_adSpecialIndex == -1 || _adSpecialIndex == _selectedRowIndex)
{
textBox.AutoCompleteCustomSource = _adSpecialList;
informationLabel.Text = @"Auto complete mode changed to Ad Special.";
}
else
{
textBox.AutoCompleteCustomSource = _trimmedAdItemCollection;
informationLabel.Text = @"An Ad Special row already exists;" + Environment.NewLine + @"auto complete mode can not be changed.";
}
}
else if (e.Modifiers == Keys.Alt && e.KeyCode == Keys.A)
{
textBox.AutoCompleteCustomSource = _trimmedAdItemCollection;
informationLabel.Text = @"Auto complete mode changed to Ad Items.";
}
e.Handled = false;
}
#endregion
#region Sales DataGridView Events
/// <summary>
/// Event Used: CellValidating
/// Validates the contents of a cell, before leaving it. If the contents
/// are valid then the appropriate formatting is applied if needed.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateSalesDataGridViewCellContents(object sender, DataGridViewCellValidatingEventArgs e)
{
//Grab the DataGirdView that fired the event and make it into a local variable.
var dataGridView = (DataGridView)sender;
var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
//Check for isNewRow if it is, return no need to check it for anything.
if (dataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
if (userInput == _beginningCellValue)
{
return;
}
//Check to make sure we're not in the boolean fields or the ID field.
if (e.ColumnIndex >= (int)TableGroupParser.SalesTableColumns.IsDirty || e.ColumnIndex == (int)TableGroupParser.SalesTableColumns.Id)
{
return;
}
//Cell validating gets to handle updating the used ad item list since it handles cells one by one, instead of by a whole row.
if (userInput != _beginningCellValue && e.ColumnIndex == (int)TableGroupParser.SalesTableColumns.AdItem)
{
//The user is trying to change the ad special text to something else.
if (e.RowIndex == _adSpecialIndex)
{
var parser = new RowParser();
if (parser.GetRowAttribute(dataGridView.Rows[e.RowIndex]) != RowParser.RowAttribute.AdSpecialRow)
{
MessageBox.Show(
@"The Ad Special row's column one (1) cannot be changed. You must delete this row by clicking on the header cell and pressing 'Delete'.",
@"Invalid Operation on Ad Special Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
dataGridView.RefreshEdit();
return;
}
//Mark all ad special members as dirty since the user changed the ad special.
for (var index = _adSpecialIndex + 1; index < dataGridView.RowCount; index++)
{
//Add overflow protection
if (index < projectionsDataGridView.RowCount)
{
if (!projectionsDataGridView.Rows[index].IsNewRow)
{
projectionsDataGridView.Rows[index].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
if (index < actualSalesDataGridView.RowCount)
{
if (!actualSalesDataGridView.Rows[index].IsNewRow)
{
actualSalesDataGridView.Rows[index].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
if (index < inventoryDataGridView.RowCount)
{
if (!inventoryDataGridView.Rows[index].IsNewRow)
{
inventoryDataGridView.Rows[index].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value =
true;
inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
}
}
else
{
_usedAdItems[_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex ? 0 : 1].Remove(_beginningCellValue);
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
}
}
else if (userInput != _beginningCellValue)
{
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
//Set the coloring for the header cell.
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
double parsedNumber;
//Check to see if the current column is the ad item column.
switch (e.ColumnIndex)
{
case (int)TableGroupParser.SalesTableColumns.AdItem: //Ad Item
//If there is text after all the whitespace has been cleared, clear the error text property regardless of whether or not it is set.
if (!string.IsNullOrEmpty(Regex.Replace(userInput, @"\s+", string.Empty)))
{
//Clear the error text since there is in fact an item entered.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
//Send the ad item text through the formatting engine and assign the new value to the cell.
userInput = TextFormat.FormatAdItemText(userInput);
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
var parser = new RowParser();
if (parser.GetRowAttribute(dataGridView.Rows[e.RowIndex]) != RowParser.RowAttribute.AdSpecialRow)
{
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
//Check to see if the ad special index has changed.
if (_adSpecialIndex == -1)
{
//Check to see if there is an ID in the row and warn the user about the row deletion.
if (
dataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue
.ToString() != string.Empty)
{
var result =
MessageBox.Show(
@"Performing this operation will clear '" + _beginningCellValue +
@"' from the database. Do you wish to continue?",
@"Delete " + _beginningCellValue + @" Permanently", MessageBoxButtons.YesNo,
MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
if (!DeleteApcRow(
int.Parse(
projectionsDataGridView.Rows[e.RowIndex].Cells[
(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString()),
int.Parse(
inventoryDataGridView.Rows[e.RowIndex].Cells[
(int) TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString()),
int.Parse(
actualSalesDataGridView.Rows[e.RowIndex].Cells[
(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString())))
{
//Error out, it failed to be cleared.
MessageBox.Show(
@"Failed to delete '" + _beginningCellValue +
@"' from the database, cannot replace ad item with " + userInput + @"'.",
@"Failed to Delete Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
return;
}
if (inventoryDataGridView.RowCount > e.RowIndex)
{
inventoryDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.InventoryTableColumns.AdItem].Value = userInput;
}
if (actualSalesDataGridView.RowCount > e.RowIndex)
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.AdItem].Value = userInput;
}
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
}
//Flag the rows under the changed row as dirty.
FlagRowsAsDirty(dataGridView, e.RowIndex + 1);
//_beginningCellValue has the ad item that was destroyed.
_usedAdItems[0].Remove(_beginningCellValue);
//Now update the UsedAdItemCollection
for (var i = e.RowIndex + 1; i < _usedAdItems[0].Count; i++)
{
//Wow this seems to work... well one shot baby!
_usedAdItems[1].Add(_usedAdItems[0][i]);
_usedAdItems[0].RemoveAt(e.RowIndex + 1);
}
//Mark row as Ad Special.
projectionsDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
TableColors.AdSpecial;
projectionsDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value =
false;
inventoryDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
TableColors.AdSpecial;
inventoryDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value
= false;
actualSalesDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor =
TableColors.AdSpecial;
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value =
false;
ClearRow(e.RowIndex);
_adSpecialIndex = e.RowIndex;
//Since this is the ad special row don't give it any color coding.
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
}
}
//Force a refresh so the cell's text updates and displays for the user.
dataGridView.RefreshEdit();
return;
}
//Otherwise, show an error.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "This column can't be blank!";
break;
case (int)TableGroupParser.SalesTableColumns.Sold:
//IF a match has been found, then make sure the word 'bin(s)' is capitalized to keep things looking pretty.
if (TextFormat.TryParseBinCount(
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(),
out double _, out string binText))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = binText;
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
dataGridView.RefreshEdit();
return;
}
//Try parsing the text entered as a number and if that fails then break out and clear the value entered.
if (double.TryParse(userInput, out parsedNumber))
{
//Add the formatted value to the cell.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
if (userInput == string.Empty)
{
return;
}
MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
break;
case (int)TableGroupParser.SalesTableColumns.SalePrice:
//If the column is the "Sale Price" column try to parse the contents to a Double and apply number formatting to the contents.
//Check for the cost column to see if there are any strings formatted like such:
var regExpression = new Regex(@"^\d+( *)?/( *)?\${0,1}?\d+(\.\d+)?", RegexOptions.IgnoreCase); // [0-9]/($)?[0-9]
//IF the current cell is in the sale price column, check for the string format above, else move to the default method.
if (regExpression.IsMatch(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString()))
{
//Grab the input and split it at the forward slash (/) for formatting.
var input = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
input = input.Replace(" ", string.Empty);
//Remove any dollar signs as these cause errors.
input = input.Replace("$", string.Empty);
var stringArray = input.Split('/');
//Format the last number as Currency, and round it up if necessary.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value =
$@"{stringArray[0]}/{Math.Round(decimal.Parse(stringArray[1]), 2):C}";
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
//Always refresh edit so the new value shows up to the user.
dataGridView.RefreshEdit();
return; //And return, there is no need to go further.
}
//Try parsing the text entered as a number and if that fails then break out and clear the value entered.
if (double.TryParse(userInput, out parsedNumber))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
if (userInput == string.Empty)
{
return;
}
MessageBox.Show(@"Column " + (e.ColumnIndex + 1) + @" only allows decimal numbers or prices in the format like '1/$10'.", @"Invalid Characters Detected");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
break;
case (int)TableGroupParser.SalesTableColumns.ProfitReturn:
if (double.TryParse(userInput, out parsedNumber))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N");
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
CalculateTotalPofitReturn(dataGridView.Rows[e.RowIndex]);
_isFormDirty = true;
}
else
{
if (userInput == string.Empty)
{
return;
}
MessageBox.Show(@"Non numeric characters are not allowed in the Profit Return column.", @"Invalid Characters Detected");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
break;
default:
//Purely here for protection against parsing the Boolean columns by mistake.
if (e.ColumnIndex == (int)TableGroupParser.SalesTableColumns.Id || e.ColumnIndex >= (int)TableGroupParser.SalesTableColumns.IsDirty) { return; }
//If the column is any other then check to see if the entered value can be parsed to a double (is a number), if not then throw an error to the user.
if (double.TryParse(userInput, out parsedNumber))
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = Math.Round(parsedNumber, 2).ToString("N", new CultureInfo("en-US"));
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
if (userInput == string.Empty)
{
return;
}
MessageBox.Show(@"Non numeric characters in column " + (e.ColumnIndex + 1) + @" are not allowed.", @"Invalid Characters Detected");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
break;
}
//Always refresh edit so the new value shows up to the user.
dataGridView.RefreshEdit();
}
#endregion
#region Projected Sales DataGridView Events
/// <summary>
/// Event Used: RowValidating
/// Checks to make sure the row is valid (has an ad item) and
/// then copies the contents where possible over to the inventory
/// and actual sales DataGridViews.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateProjectedRow(object sender, DataGridViewCellCancelEventArgs e)
{
//Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
const int adItemIndex = (int)TableGroupParser.SalesTableColumns.AdItem;
//Do not even attempt anything since this is a new row and nothing to worry about.
if (projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
//Clear all whitespace and check for a null value in the ad item column.
if (Regex.Replace(projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
{
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
e.Cancel = true;
}
//Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Projections table then that means
//the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
//a new entry to the Projections table.
if (projectionsDataGridView.RowCount - actualSalesDataGridView.RowCount > 1 || projectionsDataGridView.RowCount - inventoryDataGridView.RowCount > 1)
{
NormalizeApcTables();
return;
}
//Check to see if the user left a row that already exists and doesn't require being copied over.
if (projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
return;
}
_isFormDirty = true;
//Next check to see if the user changed the ad item is the corresponding row.
if (projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
if (projectionsDataGridView.RowCount == actualSalesDataGridView.RowCount)
{
var adItemText = projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
inventoryDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
projectionsDataGridView.RefreshEdit();
//Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
{
_usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
else
{
_usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
return;
}
}
var rowContents = new object[projectionsDataGridView.ColumnCount];
//Spin through the DataGridViewCells in the row and add their contents to an array.
for (var i = 0; i < projectionsDataGridView.ColumnCount; i++)
{
//Grab the Ad Item in the first cell and add it into the array.
switch (i)
{
case (int)TableGroupParser.SalesTableColumns.AdItem:
rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.SalePrice:
rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.Cost:
rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.ProfitReturn:
rowContents[i] = projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.IsDirty:
rowContents[i] = true;
break;
default:
rowContents[i] = string.Empty;
break;
}
}
actualSalesDataGridView.Rows.Add(rowContents);
actualSalesDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
//Build a collection of objects for the inventory table to use.
var inventoryNewRow = new object[inventoryDataGridView.ColumnCount];
//Spin through the DataGridViewCells in the row and add their contents to an array.
for (var i = 0; i < projectionsDataGridView.ColumnCount; i++)
{
//Grab the Ad Item in the first cell and add it into the array.
switch (i)
{
case (int)TableGroupParser.SalesTableColumns.Id:
inventoryNewRow[(int)TableGroupParser.InventoryTableColumns.Id] = string.Empty;
break;
case (int)TableGroupParser.SalesTableColumns.AdItem:
inventoryNewRow[(int)TableGroupParser.InventoryTableColumns.AdItem] =
projectionsDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.InventoryTableColumns.IsDirty:
inventoryNewRow[(int)TableGroupParser.InventoryTableColumns.IsDirty] = true;
break;
default:
if (i >= (int)TableGroupParser.InventoryTableColumns.IsDirty) continue;
inventoryNewRow[i] = string.Empty;
break;
}
}
inventoryDataGridView.Rows.Add(inventoryNewRow);
inventoryDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
//Set the row headers of the other tables to show up as pending; this row is valid without a doubt.
if (e.RowIndex != _adSpecialIndex)
{
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
else
{
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
}
}
private void ProjectionRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
var dataGridView = ((DataGridView)sender);
//Provide protection against overflows
if ((e.RowIndex + 1) > dataGridView.Rows.Count)
{
return;
}
//Disable all row removing events from the other two tables to prevent interference.
inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
//IF the row count on the passed DataGridView is less then the other table's row count...
if (projectionsDataGridView.RowCount <= actualSalesDataGridView.RowCount && projectionsDataGridView.RowCount <= actualSalesDataGridView.RowCount)
{
//... it is lower so its safe to assume that both tables have the same row that can be removed.
if (!actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
{
actualSalesDataGridView.Rows.RemoveAt(e.RowIndex);
}
if (!inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
{
inventoryDataGridView.Rows.RemoveAt(e.RowIndex);
}
}
//ELSE IF the row count is larger then the other table's row count...
else
{
//... Log the error and then what?
//TODO: Figure out if this is an error condition.
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Projections table has more rows then the Actual Sales table.");
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
NormalizeApcTables();
return;
}
//... After all the row removal has been finished re-enable the row removal events.
inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
//Reset the row numbers in the tables.
for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
{
projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
}
//Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
projectionsDataGridView.RefreshEdit();
inventoryDataGridView.RefreshEdit();
actualSalesDataGridView.RefreshEdit();
}
private void ToggleProjectionTableEvents(bool enable = true)
{
if (enable)
{
projectionsDataGridView.RowsAdded += DisplayRowNumbers;
projectionsDataGridView.CellEnter += StoreBeginningCellValue;
projectionsDataGridView.RowEnter += UpdateSelectedRowIndexOnEnter;
projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating += ValidateProjectedRow;
projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
projectionsDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
projectionsDataGridView.MouseMove += DataGridViewMouseMove;
projectionsDataGridView.MouseDown += DataGridViewMouseDown;
projectionsDataGridView.DragOver += DataGridViewDragOver;
projectionsDataGridView.DragDrop += DataGridViewDragDrop;
}
else
{
projectionsDataGridView.RowsAdded -= DisplayRowNumbers;
projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
projectionsDataGridView.RowEnter -= UpdateSelectedRowIndexOnEnter;
projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating -= ValidateProjectedRow;
projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
projectionsDataGridView.EditingControlShowing -= DisplayAutoCompleteOnEditingControlShowing;
projectionsDataGridView.MouseMove -= DataGridViewMouseMove;
projectionsDataGridView.MouseDown -= DataGridViewMouseDown;
projectionsDataGridView.DragOver -= DataGridViewDragOver;
projectionsDataGridView.DragDrop -= DataGridViewDragDrop;
}
}
#endregion
#region Inventory DataGridView Events
/// <summary>
/// Event Used: CellValidating
/// Validates the contents of the cell that the user is attempting to leave. Applies formatting
/// to text as needed and prevents the user from leaving invalid cells.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateInventoryCellContents(object sender, DataGridViewCellValidatingEventArgs e)
{
//Grab the DataGirdView that fired the event and make it into a local variable.
var dataGridView = ((DataGridView)sender);
var userInput = dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString();
//Check for isNewRow if it is, return no need to check it for anything.
if (dataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
if (userInput == _beginningCellValue)
{
return;
}
//Check to make sure we're not in the boolean fields or the ID field.
if (e.ColumnIndex >= (int)TableGroupParser.InventoryTableColumns.IsDirty || e.ColumnIndex == (int)TableGroupParser.InventoryTableColumns.Id)
{
return;
}
//Cell validating gets to handle updating the used ad item list since it handles cells on by one, instead by a whole row.
if (userInput != _beginningCellValue && e.ColumnIndex == (int)TableGroupParser.InventoryTableColumns.AdItem)
{
//The user is trying to change the ad special text to something else.
if (e.RowIndex == _adSpecialIndex)
{
var parser = new RowParser();
if (parser.GetRowAttribute(dataGridView.Rows[e.RowIndex]) != RowParser.RowAttribute.AdSpecialRow) //TODo: check
{
MessageBox.Show(
@"The Ad Special row's column one (1) cannot be changed. You must delete this row by clicking on the header cell and pressing 'Delete'.",
@"Invalid Operation on Ad Special Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
dataGridView.RefreshEdit();
return;
}
//Mark all ad special members as dirty since the user changed the ad special.
for (var index = _adSpecialIndex + 1; index < dataGridView.RowCount; index++)
{
//Add overflow protection
if (index < projectionsDataGridView.RowCount)
{
if (!projectionsDataGridView.Rows[index].IsNewRow)
{
projectionsDataGridView.Rows[index].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
if (index < actualSalesDataGridView.RowCount)
{
if (!actualSalesDataGridView.Rows[index].IsNewRow)
{
actualSalesDataGridView.Rows[index].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
if (index < inventoryDataGridView.RowCount)
{
if (!inventoryDataGridView.Rows[index].IsNewRow)
{
inventoryDataGridView.Rows[index].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value =
true;
inventoryDataGridView.Rows[index].HeaderCell.Style.BackColor =
TableColors.PendingEdit;
}
}
}
}
else
{
_usedAdItems[_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex ? 0 : 1].Remove(_beginningCellValue);
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
}
}
else if (userInput != _beginningCellValue)
{
dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
}
//Check to see if the current column is the ad item column.
switch (e.ColumnIndex)
{
case (int)TableGroupParser.InventoryTableColumns.AdItem:
//If there is text after all the whitespace has been cleared, clear the error text property regardless of whether or not it is set.
if (!string.IsNullOrEmpty(Regex.Replace(userInput, @"\s+", string.Empty)))
{
//Clear the error text since there is in fact an item entered.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = string.Empty;
//Send the ad item text through the formatting engine and assign the new value to the cell.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = TextFormat.FormatAdItemText(userInput);
var parser = new RowParser();
if (parser.GetRowAttribute(dataGridView.Rows[e.RowIndex]) != RowParser.RowAttribute.AdSpecialRow) //TODo: check
{
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
}
else
{
//Check to see if the ad special index has changed.
if (_adSpecialIndex == -1)
{
//Check to see if there is an ID in the row and warn the user about the row deletion.
if (dataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
var result = MessageBox.Show(@"Performing this operation will clear '" + _beginningCellValue + @"' from the database. Do you wish to continue?", @"Delete " + _beginningCellValue, MessageBoxButtons.YesNo, MessageBoxIcon.Information);
if (result == DialogResult.Yes)
{
if (DeleteApcRow(
int.Parse(
projectionsDataGridView.Rows[e.RowIndex].Cells[
(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString()),
int.Parse(
inventoryDataGridView.Rows[e.RowIndex].Cells[
(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString()),
int.Parse(
actualSalesDataGridView.Rows[e.RowIndex].Cells[
(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString())))
{
//Error out, it failed to be cleared.
MessageBox.Show(@"Failed to delete '" + _beginningCellValue + @"' from the database, cannot replace ad item with " + userInput + @"'.", @"Failed to Update Row", MessageBoxButtons.OK, MessageBoxIcon.Error);
e.Cancel = true;
return;
}
if (inventoryDataGridView.RowCount > e.RowIndex)
{
inventoryDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].Value = userInput;
}
if (actualSalesDataGridView.RowCount > e.RowIndex)
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].Value = userInput;
}
}
else
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = _beginningCellValue;
dataGridView.RefreshEdit();
e.Cancel = true;
return;
}
}
//Flag the rows under the changed row as dirty.
FlagRowsAsDirty(dataGridView, e.RowIndex + 1);
//_beginningCellValue has the ad item that was destroyed.
_usedAdItems[0].Remove(_beginningCellValue);
//Now update the UsedAdItemCollection
for (var i = e.RowIndex + 1; i < _usedAdItems[0].Count; i++)
{
//Wow this seems to work... well one shot baby!
_usedAdItems[1].Add(_usedAdItems[0][i]);
_usedAdItems[0].RemoveAt(e.RowIndex + 1);
}
//Mark row as Ad Special.
projectionsDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = TableColors.AdSpecial;
projectionsDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = false;
inventoryDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = TableColors.AdSpecial;
inventoryDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = false;
actualSalesDataGridView.Rows[e.RowIndex].DefaultCellStyle.BackColor = TableColors.AdSpecial;
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = false;
ClearRow(e.RowIndex);
_adSpecialIndex = e.RowIndex;
//Since this is the ad special row don't give it any color coding.
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
}
}
//Force a refresh so the cell's text updates and displays for the user.
dataGridView.RefreshEdit();
return;
}
//If column one (1) is blank then cancel cell validating.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].ErrorText = "This column can't be blank!";
break;
default:
if (userInput == string.Empty)
{
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
return;
}
double parsedNumber;
//Try parsing the text entered as a number and if that fails then break out and clear the value entered.
if (double.TryParse(userInput, out parsedNumber))
{
//Add the value to the cell.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = userInput;
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
if (e.ColumnIndex == (int) TableGroupParser.InventoryTableColumns.Recieved)
{
double beginningInventory;
if (
double.TryParse(
inventoryDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].EditedFormattedValue.ToString(),
out beginningInventory))
{
inventoryDataGridView.Rows[e.RowIndex].Cells[
(int) TableGroupParser.InventoryTableColumns.Total].Value = parsedNumber +
beginningInventory;
}
}
else if (e.ColumnIndex == (int) TableGroupParser.InventoryTableColumns.EndingInventory)
{
double totalInventory;
if (
double.TryParse(
inventoryDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].EditedFormattedValue
.ToString(), out totalInventory))
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.Sold].Value = totalInventory - parsedNumber;
//Calculate the total profit return.
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[e.RowIndex].Cells[
(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
CalculateTotalPofitReturn(actualSalesDataGridView.Rows[e.RowIndex]);
}
}
_isFormDirty = true;
dataGridView.RefreshEdit();
return;
}
//This Reg-ex pattern will match any number followed by the word bin(s), to allow specifying the number of bins of product were ordered.
//var inventoryStringCheck = new Regex(@"^[0-9]{1,2} \bbin(s){0,1}\b", RegexOptions.IgnoreCase);
string binText;
double binCount;
if (TextFormat.TryParseBinCount(dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].EditedFormattedValue.ToString(), out binCount, out binText))
{
//IF a match has been found, then make sure the word 'bin(s)' is capitalized to keep things looking pretty.
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = binText;
dataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
_isFormDirty = true;
//
if (e.ColumnIndex == (int)TableGroupParser.InventoryTableColumns.Recieved)
{
if (TextFormat.TryParseBinCount(inventoryDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].EditedFormattedValue.ToString(),
out double beginningBinCount, out string _))
{
var finalBinCount = binCount + beginningBinCount;
if (finalBinCount <= 0 || finalBinCount > 1)
{
inventoryDataGridView.Rows[e.RowIndex].Cells[
(int)TableGroupParser.InventoryTableColumns.Total].Value = binCount + beginningBinCount + " Bins";
}
else
{
inventoryDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.Total].Value = "1 Bin";
}
}
}
else if (e.ColumnIndex == (int)TableGroupParser.InventoryTableColumns.EndingInventory)
{
if (TextFormat.TryParseBinCount(inventoryDataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex - 1].EditedFormattedValue
.ToString(), out double totalBinCount, out string _))
{
var finalBinCount = totalBinCount - binCount;
if (finalBinCount < 1 || finalBinCount > 1)
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Sold].Value = finalBinCount + " Bins";
}
else
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.SalesTableColumns.Sold].Value = "1 Bin";
}
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[e.RowIndex].Cells[
(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
CalculateTotalPofitReturn(actualSalesDataGridView.Rows[e.RowIndex]);
}
}
dataGridView.RefreshEdit();
return;
}
break;
}
//Prevent the user from being bombarded by message boxes. All validation has been completed at this point so there's nothing to worry about.
if (userInput != string.Empty)
{
MessageBox.Show(@"Only the bin count or numbers are allowed in column " + (e.ColumnIndex + 1) + @".", @"Invalid Characters Detected");
dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = string.Empty;
dataGridView.RefreshEdit();
e.Cancel = true;
}
//Always refresh edit so the new value shows up to the user.
dataGridView.RefreshEdit();
}
/// <summary>
/// Event Used: RowValidating
/// Checks to make sure the row is valid (has an ad item) and
/// then copies the contents where possible over to the projections
/// and actual sales DataGridViews.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateInventoryRow(object sender, DataGridViewCellCancelEventArgs e)
{
//Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
const int adItemIndex = (int)TableGroupParser.InventoryTableColumns.AdItem;
//Do not even attempt anything since this is a new row and nothing to worry about.
if (inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
//Clear all whitespace and check for a null value in the ad item column.
if (Regex.Replace(inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
{
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
e.Cancel = true;
}
//Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Inventory table then that means
//the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
//a new entry to the Inventory table.
if (inventoryDataGridView.RowCount - projectionsDataGridView.RowCount > 1 || inventoryDataGridView.RowCount - actualSalesDataGridView.RowCount > 1)
{
NormalizeApcTables();
return;
}
//Check to see if the user left a row that already exists and doesn't require being copied over.
if (inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
return;
}
_isFormDirty = true;
//Next check to see if the user changed the ad item is the corresponding row.
if (inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
if (inventoryDataGridView.RowCount == actualSalesDataGridView.RowCount)
{
var adItemText = inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
projectionsDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
actualSalesDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
//Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
{
_usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
else
{
_usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
return;
}
}
var rowContents = new object[actualSalesDataGridView.ColumnCount];
//Spin through the DataGridViewCells in the row and add their contents to an array.
for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
{
//Grab the Ad Item in the first cell and add it into the array.
switch (i)
{
case (int)TableGroupParser.SalesTableColumns.AdItem:
rowContents[i] = inventoryDataGridView.Rows[e.RowIndex].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.IsDirty:
rowContents[i] = true;
break;
default:
rowContents[i] = string.Empty;
break;
}
}
projectionsDataGridView.Rows.Add(rowContents);
projectionsDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
actualSalesDataGridView.Rows.Add(rowContents);
actualSalesDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
if (e.RowIndex != _adSpecialIndex)
{
//Apply color coding to the respective row headers on the other tables.
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
else
{
//Apply color coding to the respective row headers on the other tables.
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
}
}
private void InventoryRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
var dataGridView = ((DataGridView)sender);
//Provide protection against overflows
if ((e.RowIndex + 1) > dataGridView.Rows.Count)
{
return;
}
//Disable all row removing events from the other two tables to prevent interference.
projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
//IF the row count on the passed DataGridView is less then the other table's row count...
if (inventoryDataGridView.RowCount <= projectionsDataGridView.RowCount && inventoryDataGridView.RowCount <= actualSalesDataGridView.RowCount)
{
//... it is lower so its safe to assume that both tables have the same row that can be removed.
if (!projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
{
projectionsDataGridView.Rows.RemoveAt(e.RowIndex);
}
if (!actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
{
actualSalesDataGridView.Rows.RemoveAt(e.RowIndex);
}
}
//ELSE IF the row count is larger then the other table's row count...
else
{
//... Log the error and then what?
//TODO: Figure out if this is an error condition.
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Inventory table has more rows then the Actual Sales table.");
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
NormalizeApcTables();
return;
}
//... After all the row removal has been finished re-enable the row removal events.
projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
//Reset the row numbers in each table.
for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
{
projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
}
//Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
projectionsDataGridView.RefreshEdit();
inventoryDataGridView.RefreshEdit();
actualSalesDataGridView.RefreshEdit();
}
private void ToggleInventoryEvents(bool enable = true)
{
if (enable)
{
inventoryDataGridView.RowsAdded += DisplayRowNumbers;
inventoryDataGridView.CellEnter += StoreBeginningCellValue;
inventoryDataGridView.RowEnter += UpdateSelectedRowIndexOnEnter;
inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
inventoryDataGridView.RowValidating += ValidateInventoryRow;
inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
inventoryDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
inventoryDataGridView.MouseMove += DataGridViewMouseMove;
inventoryDataGridView.MouseDown += DataGridViewMouseDown;
inventoryDataGridView.DragOver += DataGridViewDragOver;
inventoryDataGridView.DragDrop += DataGridViewDragDrop;
}
else
{
inventoryDataGridView.RowsAdded -= DisplayRowNumbers;
inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
inventoryDataGridView.RowEnter -= UpdateSelectedRowIndexOnEnter;
inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
inventoryDataGridView.RowValidating -= ValidateInventoryRow;
inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
inventoryDataGridView.EditingControlShowing -= DisplayAutoCompleteOnEditingControlShowing;
inventoryDataGridView.MouseMove -= DataGridViewMouseMove;
inventoryDataGridView.MouseDown -= DataGridViewMouseDown;
inventoryDataGridView.DragOver -= DataGridViewDragOver;
inventoryDataGridView.DragDrop -= DataGridViewDragDrop;
}
}
#endregion
#region Actual Sales DataGridView Events
/// <summary>
/// Event Used: RowValidating
/// Checks to make sure the row is valid (has an ad item) and
/// then copies the contents where possible over to the inventory
/// and actual sales DataGridViews.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateActualSalesRow(object sender, DataGridViewCellCancelEventArgs e)
{
//Grab the index of the ad item, assuming the Sales tables and the Inventory table stay in the same position.
const int adItemIndex = (int)TableGroupParser.SalesTableColumns.AdItem;
//Do not even attempt anything since this is a new row and nothing to worry about.
if (actualSalesDataGridView.Rows[e.RowIndex].IsNewRow)
{
return;
}
//Clear all whitespace and check for a null value in the ad item column.
if (Regex.Replace(actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString(), @"\s+", string.Empty) == string.Empty)
{
actualSalesDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.RowError;
MessageBox.Show(@"An ad item is required.", @"No Ad Item Specified");
actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Selected = true;
e.Cancel = true;
}
//Check to see if the other tables have more then one additional row. If the tables have two or more rows than the Actual Sales table then that means
//the tables have become unbalanced and need to be repaired. If the difference is only one row than that means the user is simply adding this row as
//a new entry to the Actual Sales table.
if (actualSalesDataGridView.RowCount - projectionsDataGridView.RowCount > 1 || actualSalesDataGridView.RowCount - inventoryDataGridView.RowCount > 1)
{
NormalizeApcTables();
return;
}
//Check to see if the user left a row that already exists and doesn't require being copied over.
if (actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() == projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
return;
}
_isFormDirty = true;
//Next check to see if the user changed the ad item is the corresponding row.
if (actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString() != projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString())
{
if (actualSalesDataGridView.RowCount == projectionsDataGridView.RowCount)
{
var adItemText = actualSalesDataGridView.Rows[e.RowIndex].Cells[adItemIndex].EditedFormattedValue.ToString();
projectionsDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
projectionsDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
inventoryDataGridView.Rows[e.RowIndex].Cells[adItemIndex].Value = adItemText;
if (e.RowIndex != _adSpecialIndex)
{
inventoryDataGridView.Rows[e.RowIndex].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
//projectionsDataGridView.RefreshEdit();
//Remove the last ad item from the UsedAdItem array, the new one will be added in the OnRowLeave event handler.
if (_adSpecialIndex == -1 || e.RowIndex < _adSpecialIndex)
{
_usedAdItems[0].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
else
{
_usedAdItems[1].RemoveAll(I => I.Equals(_beginningCellValue, StringComparison.OrdinalIgnoreCase));
}
return;
}
}
var rowContents = new object[actualSalesDataGridView.ColumnCount];
//Spin through the DataGridViewCells in the row and add their contents to an array.
for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
{
//Grab the Ad Item in the first cell and add it into the array.
switch (i)
{
case (int)TableGroupParser.SalesTableColumns.AdItem:
rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.SalePrice:
rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.Cost:
rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.ProfitReturn:
rowContents[i] = actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.IsDirty:
rowContents[i] = true;
break;
default:
rowContents[i] = string.Empty;
break;
}
}
projectionsDataGridView.Rows.Add(rowContents);
projectionsDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
//Build a collection of objects for the inventory table to use.
var inventoryNewRow = new object[inventoryDataGridView.ColumnCount];
//Spin through the DataGridViewCells in the row and add their contents to an array.
for (var i = 0; i < actualSalesDataGridView.ColumnCount; i++)
{
//Grab the Ad Item in the first cell and add it into the array.
switch (i)
{
case (int)TableGroupParser.SalesTableColumns.AdItem:
inventoryNewRow[(int)TableGroupParser.InventoryTableColumns.AdItem] =
actualSalesDataGridView.Rows[e.RowIndex].Cells[i].EditedFormattedValue.ToString();
break;
case (int)TableGroupParser.SalesTableColumns.IsDirty:
inventoryNewRow[(int)TableGroupParser.InventoryTableColumns.IsDirty] = true;
break;
default:
if (i >= (int)TableGroupParser.InventoryTableColumns.IsDirty) continue;
inventoryNewRow[i] = string.Empty;
break;
}
}
inventoryDataGridView.Rows.Add(inventoryNewRow);
inventoryDataGridView.Rows[e.RowIndex + 1].HeaderCell.Value = (e.RowIndex + 2).ToString();
if (e.RowIndex != _adSpecialIndex)
{
//Apply color coding to the respective row headers on the other tables.
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
else
{
//Apply color coding to the respective row headers on the other tables.
projectionsDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
inventoryDataGridView.Rows[e.RowIndex].HeaderCell.Style.BackColor = DefaultBackColor;
}
}
private void ActualSalesRowRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
{
var dataGridView = ((DataGridView)sender);
//Provide protection against overflows
if ((e.RowIndex + 1) > dataGridView.Rows.Count)
{
return;
}
//Disable all row removing events from the other two tables to prevent interference.
projectionsDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved -= ProjectionRowRemoved;
inventoryDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved -= InventoryRowRemoved;
//IF the row count on the passed DataGridView is less then the other table's row count...
if (actualSalesDataGridView.RowCount <= projectionsDataGridView.RowCount && actualSalesDataGridView.RowCount <= inventoryDataGridView.RowCount)
{
//... it is lower so its safe to assume that both tables have the same row that can be removed.
if (!projectionsDataGridView.Rows[e.RowIndex].IsNewRow)
{
projectionsDataGridView.Rows.RemoveAt(e.RowIndex);
}
if (!inventoryDataGridView.Rows[e.RowIndex].IsNewRow)
{
inventoryDataGridView.Rows.RemoveAt(e.RowIndex);
}
}
//ELSE IF the row count is larger then the other table's row count...
else
{
//... Log the error and then what?
//TODO: Figure out if this is an error condition.
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "The Actual Sales table has more rows then the Projections table.");
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Projections Row Count: " + projectionsDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Inventory Row Count: " + inventoryDataGridView.RowCount);
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Actual Sales Row Count: " + actualSalesDataGridView.RowCount);
errorLabel.Text = @"Error removing rows from Actual Sales and Inventory.";
NormalizeApcTables();
return;
}
//... After all the row removal has been finished re-enable the row removal events.
projectionsDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
projectionsDataGridView.RowsRemoved += ProjectionRowRemoved;
inventoryDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
inventoryDataGridView.RowsRemoved += InventoryRowRemoved;
//Reset the row numbers in the tables.
for (var i = e.RowIndex; i < (dataGridView.RowCount); i++)
{
projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
}
//Refresh the DataGridViews so the header cell's number get repainted properly and repaint the rows in case any groups get messed up during row removal.
projectionsDataGridView.RefreshEdit();
inventoryDataGridView.RefreshEdit();
actualSalesDataGridView.RefreshEdit();
}
private void ToggleActualSalesEvents(bool enable = true)
{
if (enable)
{
actualSalesDataGridView.RowsAdded += DisplayRowNumbers;
actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
actualSalesDataGridView.RowEnter += UpdateSelectedRowIndexOnEnter;
actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
actualSalesDataGridView.UserDeletingRow += UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved += ActualSalesRowRemoved;
actualSalesDataGridView.EditingControlShowing += DisplayAutoCompleteOnEditingControlShowing;
actualSalesDataGridView.MouseMove += DataGridViewMouseMove;
actualSalesDataGridView.MouseDown += DataGridViewMouseDown;
actualSalesDataGridView.DragOver += DataGridViewDragOver;
actualSalesDataGridView.DragDrop += DataGridViewDragDrop;
}
else
{
actualSalesDataGridView.RowsAdded -= DisplayRowNumbers;
actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
actualSalesDataGridView.RowEnter -= UpdateSelectedRowIndexOnEnter;
actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
actualSalesDataGridView.UserDeletingRow -= UpdateUsedAdItemCollectionOnRowRemoving;
actualSalesDataGridView.RowsRemoved -= ActualSalesRowRemoved;
actualSalesDataGridView.EditingControlShowing -= DisplayAutoCompleteOnEditingControlShowing;
actualSalesDataGridView.MouseMove -= DataGridViewMouseMove;
actualSalesDataGridView.MouseDown -= DataGridViewMouseDown;
actualSalesDataGridView.DragOver -= DataGridViewDragOver;
actualSalesDataGridView.DragDrop -= DataGridViewDragDrop;
}
}
#endregion
#region DataGridView Construction Functions
/// <summary>
/// Fills the APC DataGridViews with the appropriate columns and starting row for the user to start
/// entering data.
/// </summary>
private void ConstructApcDataGridViews()
{
//Construct a list of column names for the projections/actual sales DataGridViews and the inventory DataGirdView.
string[] saleColumnNames =
{
"ID", "AdItem", "Sold", "SalePrice", "TotalSales", "Cost", "ProfitReturn",
"TotalProfitReturn", "IsHeader", "IsMember", "IsDirty"
};
string[] inventoryColumnNames =
{
"ID", "AdItem", "BeginningInventory", "Received", "Total", "EndingInventory", "IsHeader", "IsMember", "IsDirty"
};
foreach (var name in saleColumnNames)
{
if (!name.StartsWith("Is"))
{
var column = new DataGridViewTextBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(string),
SortMode = DataGridViewColumnSortMode.NotSortable,
MaxInputLength = 32
};
if (name.Contains("ID"))
{
column.Visible = false;
}
if (name == "AdItem")
{
column.MaxInputLength = 64;
}
projectionsDataGridView.Columns.Add(column);
}
else
{
var column = new DataGridViewCheckBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(bool),
Visible = false,
SortMode = DataGridViewColumnSortMode.NotSortable
};
projectionsDataGridView.Columns.Add(column);
}
}
//Add the columns into the inventory DataGridView after setting their types.
foreach (var name in inventoryColumnNames)
{
if (!name.StartsWith("Is"))
{
var column = new DataGridViewTextBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(string),
SortMode = DataGridViewColumnSortMode.NotSortable,
MaxInputLength = 32
};
if (name.Contains("ID"))
{
column.Visible = false;
}
if (name == "AdItem")
{
column.MaxInputLength = 64;
}
inventoryDataGridView.Columns.Add(column);
}
else
{
var column = new DataGridViewCheckBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(bool),
Visible = false,
SortMode = DataGridViewColumnSortMode.NotSortable
};
inventoryDataGridView.Columns.Add(column);
}
}
//
foreach (var name in saleColumnNames)
{
if (!name.StartsWith("Is"))
{
var column = new DataGridViewTextBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(string),
SortMode = DataGridViewColumnSortMode.NotSortable,
MaxInputLength = 32
};
if (name.Contains("ID"))
{
column.Visible = false;
}
if (name == "AdItem")
{
column.MaxInputLength = 64;
}
actualSalesDataGridView.Columns.Add(column);
}
else
{
var column = new DataGridViewCheckBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(bool),
Visible = false,
SortMode = DataGridViewColumnSortMode.NotSortable
};
actualSalesDataGridView.Columns.Add(column);
}
}
}
/// <summary>
/// Constructs the invoices DataGridView.
/// </summary>
private void ConstructInvoicesDataGridView()
{
string[] invoicesColumnNames = { "ID", "InvoiceDate", "Supplier", "InvoiceNumber", "InvoiceNetAmountAtCost", "InvoiceNote", "IsDirty" };
foreach (var name in invoicesColumnNames)
{
if (!name.StartsWith("Is"))
{
var column = new DataGridViewTextBoxColumn
{
Name = name,
HeaderText = TextFormat.AddSpacesToSentence(name, false),
ValueType = typeof(string),
SortMode = DataGridViewColumnSortMode.NotSortable,
MaxInputLength = 32
};
if (name.Contains("ID"))
{
column.Visible = false;
}
if (name == "Supplier")
{
column.MaxInputLength = 64;
}
invoicesDataGridView.Columns.Add(column);
}
else
{
var column = new DataGridViewCheckBoxColumn
{
Name = name,
ValueType = typeof(bool),
Visible = false,
SortMode = DataGridViewColumnSortMode.NotSortable
};
invoicesDataGridView.Columns.Add(column);
}
}
}
#endregion
#region Comments TextBox Events
/// <summary>
/// Allows the user to select all the text in the comments text box.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void CheckForKeyCommand(object sender, KeyEventArgs e)
{
if (!e.Control || e.KeyCode != Keys.A) return;
commentsTextBox.SelectionStart = 0;
commentsTextBox.SelectionLength = commentsTextBox.Text.Length;
e.Handled = true;
e.SuppressKeyPress = true;
}
/// <summary>
/// Event Used: TextChanged
/// Calculates and displays the remaining number of characters available for the user to enter
/// and changes the color of the label displaying the character count to red when 20% or less characters remain.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void DisplayRemainingCommentCharacterCount(object sender, EventArgs e)
{
//If the amount of characters left is less then 20% (or 80% or more characters have been used) then color the label red, otherwise color it its default color.
commentsGroupBox.ForeColor = (double)commentsTextBox.TextLength / commentsTextBox.MaxLength < .8 ? default(Color) : Color.DarkRed;
commentsGroupBox.Text = @"Comments (Characters Remaining: " + (commentsTextBox.MaxLength - commentsTextBox.TextLength) + @")";
}
private void CheckForTextChangeOnLeave(object sender, EventArgs e)
{
if (commentsTextBox.Text == _beginningCellValue) return;
//Clear white spaces and check if the string is null.
if (commentsTextBox.Text.Trim() == string.Empty && isCommentDirtyCheckBox.Tag == null)
{
//The user cleared the comment(s) they were making but the comments were never committed to the database.
//So the comments are no longer dirty.
isCommentDirtyCheckBox.Checked = false;
}
else
{
//Otherwise the comment(s) were committed to the database and will need updating.
//If its empty then the record will be cleared from the database.
isCommentDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
}
private void ToggleCommentsEvents(bool enable = true)
{
if (enable)
{
commentsTextBox.Enter += StoreBeginningTextBoxValue;
commentsTextBox.KeyDown += CheckForKeyCommand;
commentsTextBox.Leave += CheckForTextChangeOnLeave;
}
else
{
commentsTextBox.Enter -= StoreBeginningTextBoxValue;
commentsTextBox.KeyDown -= CheckForKeyCommand;
commentsTextBox.Leave -= CheckForTextChangeOnLeave;
}
}
#endregion
#region Weekly Sales Events
/// <summary>
/// Event Used: Validating
/// Validates the contents of the weekly sales text boxes and adds up the total.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateWeeklySales(object sender, CancelEventArgs e)
{
var textBox = (TextBox)sender;
if (_beginningCellValue == textBox.Text) return;
//There was a change to the starting value of the text box if we've made it this far.
if (textBox.Text != string.Empty)
{
double dollarValue;
if (double.TryParse(textBox.Text.Trim(), out dollarValue))
{
//Input is valid so mark the section as dirty.
isWeeklySalesDirtyCheckBox.Checked = true;
_isFormDirty = true;
//And apply formatting.
textBox.Text = Math.Round(dollarValue, 2).ToString("N", new CultureInfo("en-US"));
//Check to see if the number is equal to zero (0) i.e. "0.00".
if (textBox.Text == @"0.00") textBox.Text = string.Empty;
}
else
{
MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
textBox.Text = string.Empty;
}
}
if (isWeeklySalesDirtyCheckBox.Tag != null)
{
isWeeklySalesDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
//Update the total sales text box before returning.
var totalWeeklySales = 0.00;
if (sundayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(sundayWeeklySalesTextBox.Text);
if (mondayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(mondayWeeklySalesTextBox.Text);
if (tuesdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(tuesdayWeeklySalesTextBox.Text);
if (wednesdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(wednesdayWeeklySalesTextBox.Text);
if (thursdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(thursdayWeeklySalesTextBox.Text);
if (fridayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(fridayWeeklySalesTextBox.Text);
if (saturdayWeeklySalesTextBox.Text != string.Empty) totalWeeklySales += double.Parse(saturdayWeeklySalesTextBox.Text);
if (Math.Abs(totalWeeklySales) > 0)
{
totalWeeklySalesTextBox.Text = totalWeeklySales.ToString("N", new CultureInfo("en-US"));
}
else
{
//Check to see if the weekly sales have been added to the database.
if (isWeeklySalesDirtyCheckBox.Tag == null)
{
//If the tag doesn't have an ID in it then we're clear to simply clear the is dirty flag.
isWeeklySalesDirtyCheckBox.Checked = false;
}
else if (isWeeklySalesDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
{
//If the user has already added the fields to the database but has removed a value
//update that accordingly.
isWeeklySalesDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
totalWeeklySalesTextBox.Text = string.Empty;
}
}
private void ToggleWeeklySalesEvents(bool enable = true)
{
if (enable)
{
sundayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
sundayWeeklySalesTextBox.Validating += ValidateWeeklySales;
mondayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
mondayWeeklySalesTextBox.Validating += ValidateWeeklySales;
tuesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
tuesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
wednesdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
wednesdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
thursdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
thursdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
fridayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
fridayWeeklySalesTextBox.Validating += ValidateWeeklySales;
saturdayWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
saturdayWeeklySalesTextBox.Validating += ValidateWeeklySales;
totalWeeklySalesTextBox.Enter += StoreBeginningTextBoxValue;
totalWeeklySalesTextBox.Validating += ValidateWeeklySales;
}
else
{
sundayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
sundayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
mondayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
mondayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
tuesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
tuesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
wednesdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
wednesdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
thursdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
thursdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
fridayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
fridayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
saturdayWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
saturdayWeeklySalesTextBox.Validating -= ValidateWeeklySales;
totalWeeklySalesTextBox.Enter -= StoreBeginningTextBoxValue;
totalWeeklySalesTextBox.Validating -= ValidateWeeklySales;
}
}
#endregion
#region Taxable Events
/// <summary>
/// Validates the contents of the Taxable text boxes and adds up the total.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateTaxableFields(object sender, CancelEventArgs e)
{
var textBox = (TextBox)sender;
if (_beginningCellValue == textBox.Text) return;
//There was a change to the starting value of the text box if we've made it this far.
if (textBox.Text != string.Empty)
{
double dollarValue;
if (double.TryParse(textBox.Text.Trim(), out dollarValue))
{
//Input is valid so mark the section as dirty.
isTaxableDirtyCheckBox.Checked = true;
_isFormDirty = true;
//And apply formatting.
textBox.Text = Math.Round(dollarValue, 2).ToString("N", new CultureInfo("en-US"));
//Check to see if the number is equal to zero (0) i.e. "0.00".
if (textBox.Text == @"0.00") textBox.Text = string.Empty;
}
else
{
MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
textBox.Text = string.Empty;
}
}
if (isTaxableDirtyCheckBox.Tag != null)
{
isTaxableDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
//Update the total sales text box before returning.
var totalTaxable = 0.00;
if (sundayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(sundayTaxableTextBox.Text);
if (mondayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(mondayTaxableTextBox.Text);
if (tuesdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(tuesdayTaxableTextBox.Text);
if (wednesdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(wednesdayTaxableTextBox.Text);
if (thursdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(thursdayTaxableTextBox.Text);
if (fridayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(fridayTaxableTextBox.Text);
if (saturdayTaxableTextBox.Text != string.Empty) totalTaxable += double.Parse(saturdayTaxableTextBox.Text);
if (Math.Abs(totalTaxable) > 0)
{
totalTaxableTextBox.Text = totalTaxable.ToString("N", new CultureInfo("en-US"));
}
else
{
//Check to see if the weekly sales have been added to the database.
if (isTaxableDirtyCheckBox.Tag == null)
{
//If the tag doesn't have an ID in it then we're clear to simply clear the is dirty flag.
isTaxableDirtyCheckBox.Checked = false;
}
else if (isTaxableDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
{
//If the user has already added the fields to the database but has removed a value
//update that accordingly.
isTaxableDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
totalTaxableTextBox.Text = string.Empty;
}
}
private void ToggleTaxableEvents(bool enable = true)
{
if (enable)
{
sundayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
sundayTaxableTextBox.Validating += ValidateTaxableFields;
mondayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
mondayTaxableTextBox.Validating += ValidateTaxableFields;
tuesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
tuesdayTaxableTextBox.Validating += ValidateTaxableFields;
wednesdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
wednesdayTaxableTextBox.Validating += ValidateTaxableFields;
thursdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
thursdayTaxableTextBox.Validating += ValidateTaxableFields;
fridayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
fridayTaxableTextBox.Validating += ValidateTaxableFields;
saturdayTaxableTextBox.Enter += StoreBeginningTextBoxValue;
saturdayTaxableTextBox.Validating += ValidateTaxableFields;
totalTaxableTextBox.Enter += StoreBeginningTextBoxValue;
totalTaxableTextBox.Validating += ValidateTaxableFields;
}
else
{
sundayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
sundayTaxableTextBox.Validating -= ValidateTaxableFields;
mondayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
mondayTaxableTextBox.Validating -= ValidateTaxableFields;
tuesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
tuesdayTaxableTextBox.Validating -= ValidateTaxableFields;
wednesdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
wednesdayTaxableTextBox.Validating -= ValidateTaxableFields;
thursdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
thursdayTaxableTextBox.Validating -= ValidateTaxableFields;
fridayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
fridayTaxableTextBox.Validating -= ValidateTaxableFields;
saturdayTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
saturdayTaxableTextBox.Validating -= ValidateTaxableFields;
totalTaxableTextBox.Enter -= StoreBeginningTextBoxValue;
totalTaxableTextBox.Validating -= ValidateTaxableFields;
}
}
#endregion
#region Cost Analysis Events
/// <summary>
/// Validates the cost analysis text boxes.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ValidateCostAnalysisValues(object sender, CancelEventArgs e)
{
var textBox = (TextBox)sender;
if (_beginningCellValue == textBox.Text) return;
//There was a change to the starting value of the text box if we've made it this far.
if (textBox.Text != string.Empty)
{
double value;
if (double.TryParse(textBox.Text.Trim(), out value))
{
//Input is valid so mark the section as dirty.
isCostAnalysisDirtyCheckBox.Checked = true;
_isFormDirty = true;
//And apply formatting.
textBox.Text = Math.Round(value, 2).ToString("N", new CultureInfo("en-US"));
//Check to see if the number is equal to zero (0) i.e. "0.00".
if (textBox.Text == @"0.00") textBox.Text = string.Empty;
}
else
{
MessageBox.Show(@"The value must be numeric.", @"Input Must be Numeric");
textBox.Text = string.Empty;
}
}
else
{
//Since the text box that fired this event is empty check to see if this group has been committed to the database.
if (isCostAnalysisDirtyCheckBox.Tag == null)
{
//Since it hasn't check to see if the other text boxes are empty.
var isDirty = salesPerManHourTextBox.Text.Trim() == string.Empty;
if (isDirty)
{
isCostAnalysisDirtyCheckBox.Checked = false;
return;
}
isDirty = salaryPercentageTextBox.Text.Trim() == string.Empty;
if (isDirty)
{
isCostAnalysisDirtyCheckBox.Checked = false;
return;
}
isDirty = salaryDollarsTextBox.Text.Trim() == string.Empty;
if (isDirty)
{
isCostAnalysisDirtyCheckBox.Checked = false;
return;
}
isDirty = suppliesTextBox.Text.Trim() == string.Empty;
if (isDirty)
{
isCostAnalysisDirtyCheckBox.Checked = false;
}
}
else if (isCostAnalysisDirtyCheckBox.Tag != null && textBox.Text == string.Empty)
{
//If the user has already added the fields to the database but has removed a value
//update that accordingly.
isCostAnalysisDirtyCheckBox.Checked = true;
_isFormDirty = true;
}
}
}
private void ToggleCostAnalysisEvents(bool enable = true)
{
if (enable)
{
salesPerManHourTextBox.Enter += StoreBeginningTextBoxValue;
salesPerManHourTextBox.Validating += ValidateCostAnalysisValues;
salaryPercentageTextBox.Enter += StoreBeginningTextBoxValue;
salaryPercentageTextBox.Validating += ValidateCostAnalysisValues;
salaryDollarsTextBox.Enter += StoreBeginningTextBoxValue;
salaryDollarsTextBox.Validating += ValidateCostAnalysisValues;
suppliesTextBox.Enter += StoreBeginningTextBoxValue;
suppliesTextBox.Validating += ValidateCostAnalysisValues;
}
else
{
salesPerManHourTextBox.Enter -= StoreBeginningTextBoxValue;
salesPerManHourTextBox.Validating -= ValidateCostAnalysisValues;
salaryPercentageTextBox.Enter -= StoreBeginningTextBoxValue;
salaryPercentageTextBox.Validating -= ValidateCostAnalysisValues;
salaryDollarsTextBox.Enter -= StoreBeginningTextBoxValue;
salaryDollarsTextBox.Validating -= ValidateCostAnalysisValues;
suppliesTextBox.Enter -= StoreBeginningTextBoxValue;
suppliesTextBox.Validating -= ValidateCostAnalysisValues;
}
}
#endregion
#region Date Time Events
private void ValidateDateChanged(object sender, DateRangeEventArgs e)
{
if (e.Start == _currentActiveDate) return;
if (!weekEndingCalendar.BoldedDates.Contains(e.Start))
{
if (_isModifyingRecord == false)
{
_currentActiveDate = e.Start;
Text = @"Add New Record (Current Record: " + e.Start.ToString("d") + @")";
}
}
else
{
//If the form is dirty then prompt the user to save changes.
if (_isFormDirty)
{
var result = MessageBox.Show(@"Would you like to save the changes you have made to this record?", @"Changes Detected", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
//Save the changes made to the database then clear and load the date picked by the user.
if (SaveRecords())
{
informationLabel.Text = @"Saved all changes successfully." + Environment.NewLine;
}
else
{
errorLabel.Text = @"Failed to save all changes." + Environment.NewLine;
return;
}
}
else if (result == DialogResult.Cancel)
{
//Cancel this method, select the active current date and return.
weekEndingCalendar.SelectionStart = _currentActiveDate;
return;
}
//The "No" button doesn't need to be processed since it just means continue on with the method.
}
_currentActiveDate = e.Start;
Text = @"Modify Record (Current Record: " + e.Start.ToString("d") + @")";
addRecordButton.Text = @"Update Record";
//Clear the form state.
ClearFormState();
//Load the specified date from the database.
LoadDate(e.Start);
}
}
private void SetNewRecordDate(DateTime date, int weeksAhead = 0)
{
weekEndingCalendar.DateChanged -= ValidateDateChanged;
switch (date.DayOfWeek)
{
case DayOfWeek.Sunday:
weekEndingCalendar.SelectionStart = date.AddDays(6 + (weeksAhead * 7));
break;
case DayOfWeek.Monday:
weekEndingCalendar.SelectionStart = date.AddDays(5 + (weeksAhead * 7));
break;
case DayOfWeek.Tuesday:
weekEndingCalendar.SelectionStart = date.AddDays(4 + (weeksAhead * 7));
break;
case DayOfWeek.Wednesday:
weekEndingCalendar.SelectionStart = date.AddDays(3 + (weeksAhead * 7));
break;
case DayOfWeek.Thursday:
weekEndingCalendar.SelectionStart = date.AddDays(2 + (weeksAhead * 7));
break;
case DayOfWeek.Friday:
weekEndingCalendar.SelectionStart = date.AddDays(1 + (weeksAhead * 7));
break;
case DayOfWeek.Saturday:
weekEndingCalendar.SelectionStart = date.AddDays((weeksAhead * 7));
break;
default:
return;
}
weekEndingCalendar.DateChanged += ValidateDateChanged;
}
#endregion
#region Form State Methods
public void InitializeForm()
{
//Apply a little nonsense fix for a but that occurs with the DataGridView control where if a cursor
//is placed over the top left hand header cell, it causes an InvalidOperationException. This mainly
//effects this form when its overloaded constructor is called since the mouse cursor is right on top
//of the aforementioned cell after clicking the "Modify Existing Record" menu option on the main form.
//More info one the bug here https://stackoverflow.com/questions/34344499/invalidoperationexception-this-operation-cannot-be-performed-while-an-auto-fill
//The following fix is applied to the projections DataGridView since its the first grid displayed to the user.
//The fix is simple however, force the form to create the cell as follows:
var projectionsLeftHeaderCell = projectionsDataGridView.TopLeftHeaderCell;
var db = new AdvertisingProfitControlDbContext();
weekEndingCalendar.BoldedDates = db.WeekEndingDates.Select(zdate => zdate.EndingDate).ToArray();
weekEndingCalendar.DateChanged += ValidateDateChanged;
//next pull all the ad items into memory.
_adItemCollection = db.AdItems.Select(x => x.Name).ToList();
//Now pull all the suppliers and the ad special list into memory.
_supplierCollection.AddRange(db.Suppliers.Select(x => x.Name).ToArray());
_adSpecialList.AddRange(db.AdSpecials.Select(x => x.Name).ToArray());
//Initialize the used ad item collection.
_usedAdItems[0] = new List<string>();
_usedAdItems[1] = new List<string>();
//Assign the events for the comments text box and display the remaining character count for the user.
commentsTextBox.TextChanged += DisplayRemainingCommentCharacterCount;
commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
//Set the text for the button that will allow the user to insert an ad special row using
//a list of ad special keywords pulled from the database. This will be easier to use then ALT + S I feel.
//https://stackoverflow.com/questions/10803184/windows-forms-button-with-drop-down-menu
insertAdSepecialRowButton.Text = @"Insert Ad Row ▼";
//Setup the events for that the Projected and Actual Sales DataGridViews will share.
//Events are assigned with regard to which event gets triggered first and so on..
//Hook the row add event so we can paint a row number in the header cell of the row.
ToggleProjectionTableEvents();
ToggleInventoryEvents();
ToggleActualSalesEvents();
//After all events have been set, construct the DataGridVeiws for use.
ConstructApcDataGridViews();
ToggleInvoicesEvents();
//Finally build the last DataGridView for the form.
ConstructInvoicesDataGridView();//No weekly sales table is nice.
//Subscribe the comments text box to check if changes have been made on leave.
ToggleCommentsEvents();
//Subscribe the weekly sales text boxes to validation, update required checks and auto-complete methods.
ToggleWeeklySalesEvents();
//Subscribe the taxable text boxes to validation and update events.
ToggleTaxableEvents();
//Subscribe the Cost Analysis text boxes to the validation and update events.
ToggleCostAnalysisEvents();
}
private void ClearFormState()
{
ToggleProjectionTableEvents(false);
ToggleInventoryEvents(false);
ToggleActualSalesEvents(false);
ToggleInvoicesEvents(false);
ToggleCommentsEvents(false);
ToggleWeeklySalesEvents(false);
ToggleTaxableEvents(false);
ToggleCostAnalysisEvents(false);
//Clear projections
projectionsDataGridView.Rows.Clear();
projectionsDataGridView.Rows[0].HeaderCell.Value = "1";
//Clear inventory
inventoryDataGridView.Rows.Clear();
inventoryDataGridView.Rows[0].HeaderCell.Value = "1";
//Clear actual sales
actualSalesDataGridView.Rows.Clear();
actualSalesDataGridView.Rows[0].HeaderCell.Value = "1";
//Reset the ad special index.
_adSpecialIndex = -1;
//Clear the beginning cell value variable.
_beginningCellValue = string.Empty;
//Reset the form's dirty flag
_isFormDirty = false;
//Clear the used ad items
_usedAdItems[0].Clear(); //regular ad items (section 1)
_usedAdItems[1].Clear(); //ad special items (section 2)
//Reset the error label
errorLabel.Text = string.Empty;
errorLabel.ForeColor = Color.Red;
//Clear invoices
invoicesDataGridView.Rows.Clear();
//Clear comments
isCommentDirtyCheckBox.Checked = false;
isCommentDirtyCheckBox.Text = @"IsCommentDirty";
isCommentDirtyCheckBox.Tag = null;
commentsTextBox.Text = string.Empty;
commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
//Clear weekly sales.
isWeeklySalesDirtyCheckBox.Checked = false;
isWeeklySalesDirtyCheckBox.Text = @"IsWeeklySalesDirty";
isWeeklySalesDirtyCheckBox.Tag = null;
sundayWeeklySalesTextBox.Text = string.Empty;
mondayWeeklySalesTextBox.Text = string.Empty;
tuesdayWeeklySalesTextBox.Text = string.Empty;
wednesdayWeeklySalesTextBox.Text = string.Empty;
thursdayWeeklySalesTextBox.Text = string.Empty;
fridayWeeklySalesTextBox.Text = string.Empty;
saturdayWeeklySalesTextBox.Text = string.Empty;
totalWeeklySalesTextBox.Text = string.Empty;
//Clear taxable.
isTaxableDirtyCheckBox.Checked = false;
isTaxableDirtyCheckBox.Text = @"IsTaxableDirty";
isTaxableDirtyCheckBox.Tag = null;
sundayTaxableTextBox.Text = string.Empty;
mondayTaxableTextBox.Text = string.Empty;
tuesdayTaxableTextBox.Text = string.Empty;
wednesdayTaxableTextBox.Text = string.Empty;
thursdayTaxableTextBox.Text = string.Empty;
fridayTaxableTextBox.Text = string.Empty;
saturdayTaxableTextBox.Text = string.Empty;
totalTaxableTextBox.Text = string.Empty;
//Clear cost analysis
isCostAnalysisDirtyCheckBox.Checked = false;
isCostAnalysisDirtyCheckBox.Text = @"IsCostAnalysisDirty";
isCostAnalysisDirtyCheckBox.Tag = null;
salesPerManHourTextBox.Text = string.Empty;
salaryPercentageTextBox.Text = string.Empty;
salaryDollarsTextBox.Text = string.Empty;
suppliesTextBox.Text = string.Empty;
//Re-enable the events for all the controls.
ToggleProjectionTableEvents();
ToggleInventoryEvents();
ToggleActualSalesEvents();
ToggleInvoicesEvents();
ToggleCommentsEvents();
ToggleWeeklySalesEvents();
ToggleTaxableEvents();
ToggleCostAnalysisEvents();
}
private void ClearRow(int rowIndex)
{
foreach (DataGridViewCell cell in projectionsDataGridView.Rows[rowIndex].Cells)
{
if (cell.ColumnIndex == (int)TableGroupParser.SalesTableColumns.AdItem) continue;
var actualSalesCell = actualSalesDataGridView.Rows[rowIndex].Cells[cell.ColumnIndex];
if (cell.ColumnIndex < (int)TableGroupParser.SalesTableColumns.IsDirty)
{
cell.Value = string.Empty;
actualSalesCell.Value = string.Empty;
}
else
{
cell.Value = false;
actualSalesCell.Value = false;
}
}
foreach (DataGridViewCell cell in inventoryDataGridView.Rows[rowIndex].Cells)
{
if (cell.ColumnIndex == (int)TableGroupParser.InventoryTableColumns.AdItem) continue;
if (cell.ColumnIndex < (int)TableGroupParser.InventoryTableColumns.IsDirty)
{
cell.Value = string.Empty;
}
else
{
cell.Value = false;
}
}
}
private void NormalizeApcTables()
{
//Disable validation events in the APC tables.
projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating -= ValidateProjectedRow;
inventoryDataGridView.RowValidating -= ValidateInventoryRow;
actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
//END DISABLE EVENTS
informationLabel.Text = string.Empty;
errorLabel.Text = @"Tables unbalanced, attempting repairs.";
var dataGridView = projectionsDataGridView.RowCount > inventoryDataGridView.RowCount
? projectionsDataGridView
: inventoryDataGridView;
dataGridView = dataGridView.RowCount > actualSalesDataGridView.RowCount
? dataGridView
: actualSalesDataGridView;
var maxRowCount = dataGridView.RowCount;
//MessageBox.Show(dataGridView.Name);
var rowParser = new RowParser();
for (var i = 0; i < maxRowCount; i++)
{
if (rowParser.GetRowAttribute(dataGridView.Rows[i]) == RowParser.RowAttribute.AdSpecialRow)
{
_adSpecialIndex = i;
}
if (maxRowCount != projectionsDataGridView.RowCount)
{
if (i < projectionsDataGridView.RowCount)
{
//Force update the existing row with whatever the fuller table has.
if (
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue !=
dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue)
{
//Force the creation of the NewRow in the DataGridView.
projectionsDataGridView.Rows.Add();
}
}
else
{
//Add the new row to the projections DataGridView
projectionsDataGridView.Rows.Insert(i - 1);
}
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue;
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
if (i != _adSpecialIndex)
{
projectionsDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
if (dataGridView == actualSalesDataGridView)
{
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].Value =
dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue;
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Cost].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue;
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue;
}
}
if (maxRowCount != inventoryDataGridView.RowCount)
{
if (i < inventoryDataGridView.RowCount)
{
//Force update the existing row with whatever the fuller table has.
if (
inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue !=
dataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue)
{
inventoryDataGridView.Rows.Add();
}
}
else
{
//Add the new row to the projections DataGridView
inventoryDataGridView.Rows.Insert(i - 1);
}
inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue;
inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
if (i != _adSpecialIndex)
{
inventoryDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
}
if (maxRowCount != actualSalesDataGridView.RowCount)
{
if (i < actualSalesDataGridView.RowCount)
{
//Force update the existing row with whatever the fuller table has.
if (
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue !=
dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue)
{
//Force the creation of the NewRow in the DataGridView.
actualSalesDataGridView.Rows.Add();
}
}
else
{
//Add the new row to the projections DataGridView
actualSalesDataGridView.Rows.Insert(i - 1);
}
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue;
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
if (i != _adSpecialIndex)
{
actualSalesDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
if (dataGridView == projectionsDataGridView)
{
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].Value =
dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue;
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Cost].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue;
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].Value = dataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue;
}
}
}
informationLabel.Text = @"Tables balanced.";
//Update the NewRow's header cell text.
projectionsDataGridView.Rows[projectionsDataGridView.RowCount - 1].HeaderCell.Value = projectionsDataGridView.RowCount.ToString();
inventoryDataGridView.Rows[inventoryDataGridView.RowCount - 1].HeaderCell.Value = inventoryDataGridView.RowCount.ToString();
actualSalesDataGridView.Rows[actualSalesDataGridView.RowCount - 1].HeaderCell.Value = actualSalesDataGridView.RowCount.ToString();
//Enable validation events in the APC tables.
projectionsDataGridView.CellEnter += StoreBeginningCellValue;
inventoryDataGridView.CellEnter += StoreBeginningCellValue;
actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating += ValidateProjectedRow;
inventoryDataGridView.RowValidating += ValidateInventoryRow;
actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
//END ENABLE EVENTS
}
#endregion
#region Data Loading Methods
private void LoadDate(DateTime date)
{
var db = new AdvertisingProfitControlDbContext();
var dateRecord = db.WeekEndingDates.SingleOrDefault(x => x.EndingDate == date);
if (dateRecord == null)
{
errorLabel.Text = @"Failed to get the date ID number, aborting load operation." + Environment.NewLine;
return;
}
informationLabel.Text = @"Loading data for " + _currentActiveDate.ToString("d") + @"." + Environment.NewLine;
LoadProjectionsTable(dateRecord);
LoadInventory(dateRecord);
LoadActualSales(dateRecord);
LoadInvoices(dateRecord);
LoadComments(dateRecord);
LoadWeeklySales(dateRecord);
LoadTaxable(dateRecord);
LoadCostAnalysis(dateRecord);
_isModifyingRecord = true;
}
private void LoadProjectionsTable(WeekEndingDate dateRecord)
{
//Assuming all the tables are correctly aligned.
//Disable the relevant events in the DataGridViews
projectionsDataGridView.CellEnter -= StoreBeginningCellValue;
projectionsDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating -= ValidateProjectedRow;
var db = new AdvertisingProfitControlDbContext();
var adSpecialIndex = -1;
var projections = db.Projections.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x).OrderBy(x => x.RowPosition);
var index = 0;
foreach (var projection in projections)
{
if (projection.FkAdSpecialId != null && adSpecialIndex == -1)
{
adSpecialIndex = index;
//Set the form's ad special index field.
_adSpecialIndex = index;
projectionsDataGridView.Rows.Insert(index, 1);
//Apply the ad special text.
projectionsDataGridView.Rows[index].DefaultCellStyle.BackColor = TableColors.AdSpecial;
var adSpecial = db.AdSpecials.Single(x => x.Id == projection.FkAdSpecialId);
projectionsDataGridView.Rows[index].Cells[1].Value = adSpecial.Name;
index++;
}
if (_adSpecialIndex == -1)
{
_usedAdItems[0].Add(projection.AdItem.Name);
}
else
{
_usedAdItems[1].Add(projection.AdItem.Name);
}
projectionsDataGridView.Rows.Add();
projectionsDataGridView.Rows[index].Cells[0].Value = projection.Id;
projectionsDataGridView.Rows[index].Cells[1].Value = projection.AdItem.Name;
projectionsDataGridView.Rows[index].Cells[2].Value = projection.Sold == "0" ? "" : projection.Sold;
projectionsDataGridView.Rows[index].Cells[3].Value = projection.SalePrice == "0" ? "" : projection.SalePrice;
projectionsDataGridView.Rows[index].Cells[4].Value = projection.TotalSales == 0 ? "" : projection.SalePrice;
projectionsDataGridView.Rows[index].Cells[5].Value = projection.Cost == 0 ? "" : projection.SalePrice;
projectionsDataGridView.Rows[index].Cells[6].Value = projection.ProfitReturn == 0 ? "" : projection.SalePrice;
projectionsDataGridView.Rows[index].Cells[7].Value = projection.TotalProfitReturn == 0 ? "" : projection.SalePrice;
projectionsDataGridView.Rows[index].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = false;
index++;
}
//Manually write the row number to the new row.
projectionsDataGridView.Rows[projectionsDataGridView.Rows.Count - 1].HeaderCell.Value = projectionsDataGridView.Rows.Count.ToString();
//Re-enable the events
projectionsDataGridView.CellEnter += StoreBeginningCellValue;
projectionsDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
projectionsDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
projectionsDataGridView.RowValidating += ValidateProjectedRow;
}
private void LoadInventory(WeekEndingDate dateRecord)
{
//Assuming all the tables are correctly aligned.
//Disable the relevant events in the DataGridViews
inventoryDataGridView.CellEnter -= StoreBeginningCellValue;
inventoryDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.CellValidating -= ValidateInventoryCellContents;
inventoryDataGridView.RowValidating -= ValidateInventoryRow;
//Create a local ad special index since Projections should have filled the ad special index in for the class.
var db = new AdvertisingProfitControlDbContext();
var adSpecialIndex = -1;
var inventories = db.Inventories.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x).OrderBy(x => x.RowPosition);
var index = 0;
foreach (var inventory in inventories)
{
if (inventory.FkAdSpecialId != null && adSpecialIndex == -1)
{
adSpecialIndex = index;
inventoryDataGridView.Rows.Insert(index, 1);
//Apply the ad special text.
inventoryDataGridView.Rows[index].DefaultCellStyle.BackColor = TableColors.AdSpecial;
var adSpecial = db.AdSpecials.Single(x => x.Id == inventory.FkAdSpecialId);
inventoryDataGridView.Rows[index].Cells[1].Value = adSpecial.Name;
index++;
}
inventoryDataGridView.Rows.Add();
inventoryDataGridView.Rows[index].Cells[0].Value = inventory.Id;
inventoryDataGridView.Rows[index].Cells[1].Value = inventory.AdItem.Name;
inventoryDataGridView.Rows[index].Cells[2].Value = inventory.BeginningInventory;
inventoryDataGridView.Rows[index].Cells[3].Value = inventory.Recieved;
inventoryDataGridView.Rows[index].Cells[4].Value = inventory.TotalInventory;
inventoryDataGridView.Rows[index].Cells[5].Value = inventory.EndingInventory;
inventoryDataGridView.Rows[index].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = false;
index++;
}
//Manually write the row number to the new row.
inventoryDataGridView.Rows[inventoryDataGridView.Rows.Count - 1].HeaderCell.Value = inventoryDataGridView.Rows.Count.ToString();
//Re-enable the events
inventoryDataGridView.CellEnter += StoreBeginningCellValue;
inventoryDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
inventoryDataGridView.CellValidating += ValidateInventoryCellContents;
inventoryDataGridView.RowValidating += ValidateInventoryRow;
}
private void LoadActualSales(WeekEndingDate dateRecord)
{
//Assuming all the tables are correctly aligned.
//Disable the relevant events in the DataGridViews
actualSalesDataGridView.CellEnter -= StoreBeginningCellValue;
actualSalesDataGridView.RowLeave -= UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.CellValidating -= ValidateSalesDataGridViewCellContents;
actualSalesDataGridView.RowValidating -= ValidateActualSalesRow;
//Create a local ad special index since Projections should have filled the ad special index in for the class.
var db = new AdvertisingProfitControlDbContext();
var adSpecialIndex = -1;
var actualSales = db.ActualSales.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x).OrderBy(x => x.RowPosition);
var index = 0;
foreach (var actualSale in actualSales)
{
if (actualSale.FkAdSpecialId != null && adSpecialIndex == -1)
{
adSpecialIndex = index;
actualSalesDataGridView.Rows.Insert(index, 1);
//Apply the ad special text.
actualSalesDataGridView.Rows[index].DefaultCellStyle.BackColor = TableColors.AdSpecial;
var adSpecial = db.AdSpecials.Single(x => x.Id == actualSale.FkAdSpecialId);
actualSalesDataGridView.Rows[index].Cells[1].Value = adSpecial.Name;
index++;
}
actualSalesDataGridView.Rows.Add();
actualSalesDataGridView.Rows[index].Cells[0].Value = actualSale.Id;
actualSalesDataGridView.Rows[index].Cells[1].Value = actualSale.AdItem.Name;
actualSalesDataGridView.Rows[index].Cells[2].Value = actualSale.Sold == "0" ? "" : actualSale.Sold;
actualSalesDataGridView.Rows[index].Cells[3].Value = actualSale.SalePrice == "0" ? "" : actualSale.SalePrice;
actualSalesDataGridView.Rows[index].Cells[4].Value = actualSale.TotalSales == 0 ? "" : actualSale.SalePrice;
actualSalesDataGridView.Rows[index].Cells[5].Value = actualSale.Cost == 0 ? "" : actualSale.SalePrice;
actualSalesDataGridView.Rows[index].Cells[6].Value = actualSale.ProfitReturn == 0 ? "" : actualSale.SalePrice;
actualSalesDataGridView.Rows[index].Cells[7].Value = actualSale.TotalProfitReturn == 0 ? "" : actualSale.SalePrice;
actualSalesDataGridView.Rows[index].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = false;
index++;
}
//Manually write the row number to the new row.
actualSalesDataGridView.Rows[actualSalesDataGridView.Rows.Count - 1].HeaderCell.Value = actualSalesDataGridView.Rows.Count.ToString();
//Re-enable the events
actualSalesDataGridView.CellEnter += StoreBeginningCellValue;
actualSalesDataGridView.RowLeave += UpdateUsedAdItemCollectionOnRowLeave;
actualSalesDataGridView.CellValidating += ValidateSalesDataGridViewCellContents;
actualSalesDataGridView.RowValidating += ValidateActualSalesRow;
}
private void LoadInvoices(WeekEndingDate dateRecord)
{
var db = new AdvertisingProfitControlDbContext();
var invoices = db.Invoices.Where(x => x.WeekEndingDate.Id == dateRecord.Id).Select(x => x);
var index = 0;
foreach (var invoice in invoices)
{
invoicesDataGridView.Rows.Add();
invoicesDataGridView.Rows[index].Cells[0].Value = invoice.Id;
invoicesDataGridView.Rows[index].Cells[2].Value = invoice.Supplier.Name;
invoicesDataGridView.Rows[index].Cells[1].Value = invoice.InvoiceDate.ToString("d");
invoicesDataGridView.Rows[index].Cells[3].Value = invoice.InvoiceNumber;
invoicesDataGridView.Rows[index].Cells[4].Value = invoice.InvoiceNetAmountAtCost == 0 ? "" : invoice.InvoiceNetAmountAtCost.ToString();
invoicesDataGridView.Rows[index].Cells[5].Value = invoice.InvoiceNote;
invoicesDataGridView.Rows[index].Cells[(int)TableGroupParser.InvoiceTableColumns.IsDirty].Value = false;
index++;
}
}
private void LoadComments(WeekEndingDate dateRecord)
{
//commentsTextBox.TextChanged -= DisplayRemainingCommentCharacterCount;
commentsTextBox.Enter -= StoreBeginningTextBoxValue;
commentsTextBox.KeyDown -= CheckForKeyCommand;
commentsTextBox.Leave -= CheckForTextChangeOnLeave;
var db = new AdvertisingProfitControlDbContext();
var comments = db.Notes.SingleOrDefault(x => x.FkDateId == dateRecord.Id);
if (comments != null)
{
isCommentDirtyCheckBox.Tag = comments.Id;
isCommentDirtyCheckBox.Text = @"IsCommentsDirty (" + comments.Id + @")";
commentsTextBox.Text = comments.Remark;
}
else
{
informationLabel.Text += @"No comments to display." + Environment.NewLine;
}
//commentsGroupBox.Text = @"Comments (Characters Remaining: " + commentsTextBox.MaxLength + @")";
//commentsTextBox.TextChanged += DisplayRemainingCommentCharacterCount;
commentsTextBox.Enter += StoreBeginningTextBoxValue;
commentsTextBox.KeyDown += CheckForKeyCommand;
commentsTextBox.Leave += CheckForTextChangeOnLeave;
}
private void LoadWeeklySales(WeekEndingDate dateRecord)
{
ToggleWeeklySalesEvents(false);
var db = new AdvertisingProfitControlDbContext();
var weeklySale = db.WeeklySales.SingleOrDefault(x => x.FkDateId == dateRecord.Id);
if (weeklySale == null)
{
informationLabel.Text += @"No weekly sales to display." + Environment.NewLine;
}
else
{
sundayWeeklySalesTextBox.Text = weeklySale.Sunday == 0 ? string.Empty : $"{weeklySale.Sunday:N2}";
mondayWeeklySalesTextBox.Text = weeklySale.Monday == 0 ? string.Empty : $"{weeklySale.Monday:N2}";
tuesdayWeeklySalesTextBox.Text = weeklySale.Tuesday == 0 ? string.Empty : $"{weeklySale.Tuesday:N2}";
wednesdayWeeklySalesTextBox.Text = weeklySale.Wednesday == 0 ? string.Empty : $"{weeklySale.Wednesday:N2}";
thursdayWeeklySalesTextBox.Text = weeklySale.Thursday == 0 ? string.Empty : $"{weeklySale.Thursday:N2}";
fridayWeeklySalesTextBox.Text = weeklySale.Friday == 0 ? string.Empty : $"{weeklySale.Friday:N2}";
saturdayWeeklySalesTextBox.Text = weeklySale.Saturday == 0 ? string.Empty : $"{weeklySale.Saturday:N2}";
totalWeeklySalesTextBox.Text = weeklySale.TotalSales == 0 ? string.Empty : $"{weeklySale.TotalSales:N2}";
//Set the internal state
isWeeklySalesDirtyCheckBox.Text = @"IsWeeklySalesDirty (" + weeklySale.Id + @")";
isWeeklySalesDirtyCheckBox.Tag = weeklySale.Id;
}
//Re-enable the events
ToggleWeeklySalesEvents();
}
private void LoadTaxable(WeekEndingDate dateRecord)
{
ToggleTaxableEvents(false);
var db = new AdvertisingProfitControlDbContext();
var taxable = db.Taxables.SingleOrDefault(x => x.FkDateId == dateRecord.Id);
if (taxable == null)
{
informationLabel.Text += @"No taxables to display." + Environment.NewLine;
}
else
{
sundayTaxableTextBox.Text = taxable.Sunday == 0 ? string.Empty : $"{taxable.Sunday:N2}";
mondayTaxableTextBox.Text = taxable.Monday == 0 ? string.Empty : $"{taxable.Monday:N2}";
tuesdayTaxableTextBox.Text = taxable.Tuesday == 0 ? string.Empty : $"{taxable.Tuesday:N2}";
wednesdayTaxableTextBox.Text = taxable.Wednesday == 0 ? string.Empty : $"{taxable.Wednesday:N2}";
thursdayTaxableTextBox.Text = taxable.Thursday == 0 ? string.Empty : $"{taxable.Thursday:N2}";
fridayTaxableTextBox.Text = taxable.Friday == 0 ? string.Empty : $"{taxable.Friday:N2}";
saturdayTaxableTextBox.Text = taxable.Saturday == 0 ? string.Empty : $"{taxable.Saturday:N2}";
totalTaxableTextBox.Text = taxable.Total == 0 ? string.Empty : $"{taxable.Total:N2}";
//Set the internal state
isTaxableDirtyCheckBox.Text = @"IsTaxableDirty (" + taxable.Id + @")";
isTaxableDirtyCheckBox.Tag = taxable.Id;
}
ToggleTaxableEvents();
}
private void LoadCostAnalysis(WeekEndingDate dateRecord)
{
ToggleCostAnalysisEvents(false);
var db = new AdvertisingProfitControlDbContext();
var costAnalysis = db.CostAnalysis.SingleOrDefault(x => x.FkDateId == dateRecord.Id);
if (costAnalysis == null)
{
informationLabel.Text += @"No cost analysis to display." + Environment.NewLine;
}
else
{
salaryDollarsTextBox.Text = costAnalysis.SalaryDollar == 0 ? string.Empty : costAnalysis.SalaryDollar.ToString();
salesPerManHourTextBox.Text = costAnalysis.SalesPerManHour == 0 ? string.Empty : costAnalysis.SalesPerManHour.ToString();
salaryPercentageTextBox.Text = costAnalysis.SalaryPercentage == 0 ? string.Empty : costAnalysis.SalaryPercentage.ToString();
suppliesTextBox.Text = costAnalysis.Supplies == 0 ? string.Empty : costAnalysis.Supplies.ToString();
//Set the internal state
isCostAnalysisDirtyCheckBox.Text = @"IsCostAnalysisDirty (" + costAnalysis.Id + @")";
isCostAnalysisDirtyCheckBox.Tag = costAnalysis.Id;
}
ToggleCostAnalysisEvents();
}
#endregion
#region Database Update/Insertion Methods
/// <summary>
/// Saves all changes made to the form to the database.
/// </summary>
/// <returns>True on success otherwise, false.</returns>
private bool SaveRecords()
{
var success = true;
var db = new AdvertisingProfitControlDbContext();
//Get the date ID for the current active date.
if (_currentActiveDate.DayOfWeek != DayOfWeek.Saturday)
{
SetNewRecordDate(_currentActiveDate);
_currentActiveDate = weekEndingCalendar.SelectionStart;
//See if the date is in the database already.
if (weekEndingCalendar.BoldedDates.Contains(_currentActiveDate))
{
//Loop till we hit a date not in the system.
var weekAhead = 1;
while (weekEndingCalendar.BoldedDates.Contains(_currentActiveDate.AddDays(7 * weekAhead)))
{
weekAhead++;
}
var result = MessageBox.Show(@"The date " + _currentActiveDate.ToShortDateString() + @" is already in the database. Did you mean to select " + _currentActiveDate.AddDays(7 * weekAhead).ToShortDateString() + @" instead?", @"", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
_currentActiveDate = _currentActiveDate.AddDays(7 * weekAhead);
weekEndingCalendar.SelectionStart = _currentActiveDate;
}
else if (result == DialogResult.No)
{
errorLabel.Text = _currentActiveDate.ToShortDateString() +
@" must be loaded before modifications can be made to it.";
return false;
}
else
{
return false;
}
}
}
//Create the DateTime object that represents the week ending date key.
var date = new WeekEndingDate
{
EndingDate = weekEndingCalendar.SelectionStart
};
try
{
if (!db.WeekEndingDates.Any(x => x.EndingDate == weekEndingCalendar.SelectionStart))
{
db.WeekEndingDates.Add(date);
db.SaveChanges();
}
else
{
date = db.WeekEndingDates.Single(x => x.EndingDate == weekEndingCalendar.SelectionStart);
}
}
catch (DbUpdateException e)
{
errorLabel.Text = @"Failed to update the current date " + _currentActiveDate.ToString("d") + @".";
informationLabel.Text = @"Record updating aborted.";
LogConsole.WriteToLog(FrmLogConsole.Level.Critical, e.Message);
return false;
}
//Clear the error and information labels.
informationLabel.Text = string.Empty;
errorLabel.Text = string.Empty;
try
{
using (var scope = new TransactionScope())
{
SaveProjections(date);
SaveInventory(date);
SaveActualSales(date);
scope.Complete();
}
//All completed without error so mark all the APC table rows as committed.
for (var i = 0; i < projectionsDataGridView.RowCount - 1; i++)
{
//Prevent the ad special row from being marked in any way.
if (i == _adSpecialIndex) continue;
//Prevent rows pulled from that the database, that haven't been touched, from being marked as well.
if ((bool) projectionsDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].EditedFormattedValue && projectionsDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
projectionsDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = false;
projectionsDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.EditingSaved;
}
//Prevent rows pulled from that the database, that haven't been touched, from being marked as well.
if ((bool) inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].EditedFormattedValue && inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
inventoryDataGridView.Rows[i].Cells[(int) TableGroupParser.InventoryTableColumns.IsDirty].Value = false;
inventoryDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.EditingSaved;
}
//Prevent rows pulled from that the database, that haven't been touched, from being marked as well.
if (!(bool)
actualSalesDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].EditedFormattedValue && actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
continue;
actualSalesDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].Value = false;
actualSalesDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.EditingSaved;
}
}
catch (TransactionAbortedException e)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.Message);
informationLabel.Text = e.Message;
success = false;
//Spin through all the rows and check to see if their IDs are in the database.
for (var i = 0; i < projectionsDataGridView.RowCount - 1; i++)
{
if (projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
var id = int.Parse(projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString());
if (!db.Projections.Any(x => x.Id == id))
{
projectionsDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.Id].Value = string.Empty;
}
}
if (inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
{
var id = int.Parse(inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString());
if (!db.Inventories.Any(x => x.Id == id))
{
inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.Id].Value = string.Empty;
}
}
if (actualSalesDataGridView.Rows[i].Cells[(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() ==
string.Empty) continue;
{
var id = int.Parse(actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString());
if (!db.ActualSales.Any(x => x.Id == id))
{
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.Id].Value = string.Empty;
}
}
}
}
//Save the other bits of data.
SaveInvoices(date);
SaveWeeklySales(date);
SaveTaxable(date);
SaveCostAnalysis(date);
SaveComments(date);
//If everything went through properly then set the form as not dirty since all changes are saved.
_isFormDirty = false;
addRecordButton.Text = @"Update Record";
Text = @"Modify Record (Current Record: " + _currentActiveDate.ToShortDateString() + @")";
_isModifyingRecord = true;
weekEndingCalendar.AddBoldedDate(_currentActiveDate);
weekEndingCalendar.Invalidate();
return success;
}
private void SaveProjections(WeekEndingDate date)
{
var db = new AdvertisingProfitControlDbContext();
var lastRow = -1;
using (var scope = new TransactionScope())
{
try
{
//Create the ad special object in-case one is in countered.
var adSpecial = new AdSpecial();
//Row index, projection object
var idNumbers = new Dictionary<int, Projection>();
//Spin through the Projections DataGridView and build Projection objects.
foreach (DataGridViewRow row in projectionsDataGridView.Rows)
{
//If the row is a new row or is not dirty then continue to the next row if applicable.
if (row.IsNewRow) continue;
if (!(bool) row.Cells[(int) TableGroupParser.SalesTableColumns.IsDirty].EditedFormattedValue && row.Index != _adSpecialIndex) continue;
//Obtain the ad item.
var adItem = new AdItem();
//Sadly, LINQ doesn't like calls to the ToString method so we create a temp variable.
var adItemName = row.Cells[(int) TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString();
//Check to see if this is the ad special row, and if so create the object.
if (row.Index == _adSpecialIndex)
{
adSpecial = GetAdSpecial(adItemName);
//Check to see if the ad special return value exists.
if (adSpecial.Id == 0)
{
//Warn the user about the error and break out of the loop.
//We can simply ignore the ad special group and write the rest to the database.
errorLabel.Text = @"Warning: ad special group '" + adItemName + @"' failed to" + Environment.NewLine + @"write to the database." + Environment.NewLine + @"All rows starting from " + (_adSpecialIndex + 1) + @" to " + (projectionsDataGridView.RowCount - 1) + @" will not be saved.";
errorLabel.ForeColor = Color.Orange;
break;
}
continue;
}
//Check to see if the ad item is in the database and if so grab the object for it, otherwise add it.
if (db.AdItems.Any(x => x.Name == adItemName))
{
//Match
adItem = db.AdItems.Single(x => x.Name == adItemName);
}
else
{
//Not a match
adItem.Name = row.Cells[(int) TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString();
db.AdItems.Add(adItem);
db.SaveChanges();
}
var projection = new Projection();
//Apply ad special status if there is one.
if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
{
projection.FkAdSpecialId = adSpecial.Id;
}
else
{
projection.FkAdSpecialId = null;
}
//Check for an ID number
if (row.Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() == string.Empty)
{
//If there isn't one then create a new record and add it to the database.
projection.Sold = row.Cells[(int) TableGroupParser.SalesTableColumns.Sold].EditedFormattedValue.ToString();
projection.SalePrice =
row.Cells[(int) TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue.ToString();
projection.TotalSales =
string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString());
projection.Cost =
string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString());
projection.ProfitReturn =
string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue
.ToString());
projection.TotalProfitReturn =
string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString());
projection.FkAdItemId = adItem.Id;
projection.RowPosition = row.Index + 1;
projection.FkDateId = date.Id;
db.Projections.Add(projection);
idNumbers.Add(row.Index, projection);
}
else
{
//If there is one, set the current object to reference the existing record and update.
projection = db.Projections.Find(int.Parse(row.Cells[(int) TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString()));
if (projection == null) throw new DbUpdateException("Failed to retrieve Projection record.");
projection.Sold = row.Cells[(int) TableGroupParser.SalesTableColumns.Sold].EditedFormattedValue.ToString();
projection.SalePrice =
row.Cells[(int) TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue.ToString();
projection.TotalSales = string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString());
projection.Cost = string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString());
projection.ProfitReturn = string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString());
projection.TotalProfitReturn = string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString());
projection.FkAdSpecialId = adSpecial.Id == 0 ? (int?) null : adSpecial.Id;
projection.FkAdItemId = adItem.Id;
//Hard coded value to prevent legacy values from interfering.
//TODO: Remove this un-needed line.
projection.RowAttribute = 0;
projection.RowPosition = row.Index + 1;
}
//Update the last row that was done before jumping to the next loop.
lastRow = row.Index;
}
if (lastRow != -1)
{
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
foreach (var index in idNumbers)
{
projectionsDataGridView.Rows[index.Key].Cells[(int) TableGroupParser.SalesTableColumns.Id].Value = index.Value.Id;
}
informationLabel.Text = @"Updated the Projections table." + Environment.NewLine;
}
else
{
informationLabel.Text = @"No changes detected in Projections." + Environment.NewLine;
}
scope.Complete();
}
catch (DbUpdateException e)
{
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
throw new TransactionAbortedException("Failed to update Projections." + Environment.NewLine + "Updates to Inventory aborted." + Environment.NewLine + "Updates to Actual Sales aborted." + Environment.NewLine);
}
}
}
private void SaveInventory(WeekEndingDate date)
{
var db = new AdvertisingProfitControlDbContext();
var lastRow = -1;
using (var scope = new TransactionScope())
{
try
{
//Create the ad special object in-case one is in countered.
var adSpecial = new AdSpecial();
//Row index, inventory object
var idNumbers = new Dictionary<int, Inventory>();
//Spin through the Projections DataGridView and build Projection objects.
foreach (DataGridViewRow row in inventoryDataGridView.Rows)
{
//If the row is a new row or is not dirty then continue to the next row if applicable.
if (row.IsNewRow) continue;
if (!(bool)row.Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].EditedFormattedValue && row.Index != _adSpecialIndex) continue;
//Obtain the ad item.
var adItem = new AdItem();
//Sadly, LINQ doesn't like calls to the ToString method so we create a temp variable.
var adItemName = row.Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue.ToString();
//Check to see if this is the ad special row, and if so create the object.
if (row.Index == _adSpecialIndex)
{
adSpecial = GetAdSpecial(adItemName);
//Check to see if the ad special return value exists.
if (adSpecial.Id == 0)
{
//Warn the user about the error and break out of the loop.
//We can simply ignore the ad special group and write the rest to the database.
errorLabel.Text = @"Warning: ad special group '" + adItemName + @"' failed to" + Environment.NewLine + @"write to the database." + Environment.NewLine + @"All rows starting from " + (_adSpecialIndex + 1) + @" to " + (projectionsDataGridView.RowCount - 1) + @" will not be saved.";
errorLabel.ForeColor = Color.Orange;
break;
}
continue;
}
//Check to see if the ad item is in the database and if so grab the object for it, otherwise add it.
if (db.AdItems.Any(x => x.Name == adItemName))
{
//Match
adItem = db.AdItems.Single(x => x.Name == adItemName);
}
else
{
//Not a match
adItem.Name = row.Cells[(int)TableGroupParser.InventoryTableColumns.AdItem].EditedFormattedValue.ToString();
db.AdItems.Add(adItem);
db.SaveChanges();
}
var inventory = new Inventory();
//Apply ad special status if there is one.
if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
{
inventory.FkAdSpecialId = adSpecial.Id;
}
else
{
inventory.FkAdSpecialId = null;
}
//Check for an ID number
if (row.Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString() == string.Empty)
{
//If there isn't one then create a new record and add it to the database.
inventory.BeginningInventory =
row.Cells[(int) TableGroupParser.InventoryTableColumns.BeginningInventory].EditedFormattedValue.ToString();
inventory.Recieved =
row.Cells[(int) TableGroupParser.InventoryTableColumns.Recieved].EditedFormattedValue.ToString();
inventory.TotalInventory =
row.Cells[(int) TableGroupParser.InventoryTableColumns.Total].EditedFormattedValue.ToString();
inventory.EndingInventory =
row.Cells[(int) TableGroupParser.InventoryTableColumns.EndingInventory].EditedFormattedValue.ToString();
inventory.FkAdItemId = adItem.Id;
inventory.RowPosition = row.Index + 1;
inventory.FkDateId = date.Id;
db.Inventories.Add(inventory);
idNumbers.Add(row.Index, inventory);
}
else
{
//If there is one, set the current object to reference the existing record and update.
inventory = db.Inventories.Find(int.Parse(row.Cells[(int)TableGroupParser.InventoryTableColumns.Id].EditedFormattedValue.ToString()));
if (inventory == null) throw new DbUpdateException("Failed to retrieve Inventory record.");
inventory.BeginningInventory =
row.Cells[(int)TableGroupParser.InventoryTableColumns.BeginningInventory].EditedFormattedValue.ToString();
inventory.Recieved =
row.Cells[(int)TableGroupParser.InventoryTableColumns.Recieved].EditedFormattedValue.ToString();
inventory.TotalInventory =
row.Cells[(int)TableGroupParser.InventoryTableColumns.Total].EditedFormattedValue.ToString();
inventory.EndingInventory =
row.Cells[(int)TableGroupParser.InventoryTableColumns.EndingInventory].EditedFormattedValue.ToString();
inventory.FkAdSpecialId = adSpecial.Id == 0 ? (int?)null : adSpecial.Id;
inventory.FkAdItemId = adItem.Id;
//Hard coded value to prevent legacy values from interfering.
//TODO: Remove this un-needed line.
inventory.RowAttribute = 0;
inventory.RowPosition = row.Index + 1;
}
//Update the last row that was done before jumping to the next loop.
lastRow = row.Index;
}
if (lastRow != -1)
{
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
foreach (var index in idNumbers)
{
inventoryDataGridView.Rows[index.Key].Cells[(int) TableGroupParser.InventoryTableColumns.Id].Value = index.Value.Id;
}
informationLabel.Text += @"Updated the Inventory table." + Environment.NewLine;
}
else
{
informationLabel.Text += @"No changes detected in Inventory." + Environment.NewLine;
}
scope.Complete();
}
catch (DbUpdateException e)
{
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
throw new TransactionAbortedException("Updates to Projections aborted." + Environment.NewLine + "Failed to update Inventory." + Environment.NewLine + "Updates to Actual Sales aborted." + Environment.NewLine);
}
}
}
private void SaveActualSales(WeekEndingDate date)
{
var db = new AdvertisingProfitControlDbContext();
var lastRow = -1;
using (var scope = new TransactionScope())
{
try
{
//Create the ad special object in-case one is in countered.
var adSpecial = new AdSpecial();
//Row index, ActualSale object
var idNumbers = new Dictionary<int, ActualSale>();
//Spin through the Projections DataGridView and build Projection objects.
foreach (DataGridViewRow row in actualSalesDataGridView.Rows)
{
//If the row is a new row or is not dirty then continue to the next row if applicable.
if (row.IsNewRow) continue;
if (!(bool)row.Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].EditedFormattedValue && row.Index != _adSpecialIndex) continue;
//Obtain the ad item.
var adItem = new AdItem();
//Sadly, LINQ doesn't like calls to the ToString method so we create a temp variable.
var adItemName = row.Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString();
//Check to see if this is the ad special row, and if so create the object.
if (row.Index == _adSpecialIndex)
{
adSpecial = GetAdSpecial(adItemName);
//Check to see if the ad special return value exists.
if (adSpecial.Id == 0)
{
//Warn the user about the error and break out of the loop.
//We can simply ignore the ad special group and write the rest to the database.
errorLabel.Text = @"Warning: ad special group '" + adItemName + @"' failed to" + Environment.NewLine + @"write to the database." + Environment.NewLine + @"All rows starting from " + (_adSpecialIndex + 1) + @" to " + (projectionsDataGridView.RowCount - 1) + @" will not be saved.";
errorLabel.ForeColor = Color.Orange;
break;
}
continue;
}
//Check to see if the ad item is in the database and if so grab the object for it, otherwise add it.
if (db.AdItems.Any(x => x.Name == adItemName))
{
//Match
adItem = db.AdItems.Single(x => x.Name == adItemName);
}
else
{
//Not a match
adItem.Name = row.Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue.ToString();
db.AdItems.Add(adItem);
db.SaveChanges();
}
var actualSale = new ActualSale();
//Apply ad special status if there is one.
if (_adSpecialIndex != -1 && row.Index > _adSpecialIndex)
{
actualSale.FkAdSpecialId = adSpecial.Id;
}
else
{
actualSale.FkAdSpecialId = null;
}
//Check for an ID number
if (row.Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString() == string.Empty)
{
//If there isn't one then create a new record and add it to the database.
actualSale.Sold = row.Cells[(int)TableGroupParser.SalesTableColumns.Sold].EditedFormattedValue.ToString();
actualSale.SalePrice =
row.Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue.ToString();
actualSale.TotalSales =
string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString());
actualSale.Cost =
string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString());
actualSale.ProfitReturn =
string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue
.ToString());
actualSale.TotalProfitReturn =
string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString());
actualSale.FkAdItemId = adItem.Id;
//Hard coded value to prevent legacy values from interfering.
//TODO: Remove this un-needed line.
actualSale.RowAttribute = 0;
actualSale.RowPosition = row.Index + 1;
actualSale.FkDateId = date.Id;
db.ActualSales.Add(actualSale);
idNumbers.Add(row.Index, actualSale);
}
else
{
//If there is one, set the current object to reference the existing record and update.
actualSale = db.ActualSales.Find(int.Parse(row.Cells[(int)TableGroupParser.SalesTableColumns.Id].EditedFormattedValue.ToString()));
if (actualSale == null) throw new DbUpdateException("Failed to retrieve Actual Sales record.");
actualSale.Sold = row.Cells[(int)TableGroupParser.SalesTableColumns.Sold].EditedFormattedValue.ToString();
actualSale.SalePrice =
row.Cells[(int)TableGroupParser.SalesTableColumns.SalePrice].EditedFormattedValue.ToString();
actualSale.TotalSales = string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalSales].EditedFormattedValue.ToString());
actualSale.Cost = string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.Cost].EditedFormattedValue.ToString());
actualSale.ProfitReturn = string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString());
actualSale.TotalProfitReturn = string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.SalesTableColumns.TotalProfitReturn].EditedFormattedValue
.ToString());
actualSale.FkAdSpecialId = adSpecial.Id == 0 ? (int?)null : adSpecial.Id;
actualSale.FkAdItemId = adItem.Id;
actualSale.RowPosition = row.Index + 1;
}
//Update the last row that was done before jumping to the next loop.
lastRow = row.Index;
}
if (lastRow != -1)
{
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
foreach (var index in idNumbers)
{
actualSalesDataGridView.Rows[index.Key].Cells[(int) TableGroupParser.SalesTableColumns.Id].Value = index.Value.Id;
}
informationLabel.Text += @"Updated the Actual Sales table." + Environment.NewLine;
}
else
{
informationLabel.Text += @"No changes detected in Actual Sales." + Environment.NewLine;
}
scope.Complete();
}
catch (DbUpdateException e)
{
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
throw new TransactionAbortedException("Updates to Projections aborted." + Environment.NewLine +
"Updates to Inventory aborted." + Environment.NewLine +
"Failed to update Actual Sales" + Environment.NewLine);
}
}
}
private void SaveInvoices(WeekEndingDate date)
{
var db = new AdvertisingProfitControlDbContext();
var lastRow = -1;
using (var scope = new TransactionScope())
{
try
{
var idNumbers = new Dictionary<int, Invoice>();
//Spin through the Projections DataGridView and build Projection objects.
foreach (DataGridViewRow row in invoicesDataGridView.Rows)
{
//If the row is a new row or is not dirty then continue to the next row if applicable.
if (row.IsNewRow) continue;
if (!(bool)row.Cells[(int) TableGroupParser.InvoiceTableColumns.IsDirty].EditedFormattedValue) continue;
//Obtain the ad item.
var supplier = new Supplier();
//Sadly, LINQ doesn't like calls to the ToString method so we create a temp variable.
var supplierName = row.Cells[(int)TableGroupParser.InvoiceTableColumns.Supplier].EditedFormattedValue.ToString();
//Check to see if the ad item is in the database and if so grab the object for it, otherwise add it.
if (db.Suppliers.Any(x => x.Name == supplierName))
{
//Match
supplier = db.Suppliers.Single(x => x.Name == supplierName);
}
else
{
//Not a match
supplier.Name = row.Cells[(int)TableGroupParser.InvoiceTableColumns.Supplier].EditedFormattedValue.ToString();
db.Suppliers.Add(supplier);
db.SaveChanges();
}
var invoice = new Invoice();
//Check for an ID number
if (row.Cells[(int)TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue.ToString() == string.Empty)
{
//If there isn't one then create a new record and add it to the database.
invoice.InvoiceDate = DateTime.Parse(row.Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString());
invoice.InvoiceNumber = row.Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNumber].EditedFormattedValue.ToString();
invoice.InvoiceNetAmountAtCost =
string.IsNullOrWhiteSpace(
row.Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNetAmountAtCost].EditedFormattedValue.ToString())
? 0
: decimal.Parse(
row.Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNetAmountAtCost].EditedFormattedValue.ToString());
invoice.InvoiceNote = row.Cells[(int)TableGroupParser.InvoiceTableColumns.InvoiceNote].EditedFormattedValue.ToString();
invoice.FkSupplierId = supplier.Id;
invoice.FkDateId = date.Id;
db.Invoices.Add(invoice);
idNumbers.Add(row.Index, invoice);
}
else
{
//If there is one, set the current object to reference the existing record and update.
invoice = db.Invoices.Find(int.Parse(row.Cells[(int)TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue.ToString()));
if (invoice != null)
{
invoice.InvoiceDate =
DateTime.Parse(
row.Cells[(int) TableGroupParser.InvoiceTableColumns.InvoiceDate].EditedFormattedValue.ToString());
invoice.InvoiceNumber = row.Cells[(int) TableGroupParser.InvoiceTableColumns.InvoiceNumber].EditedFormattedValue.ToString();
invoice.InvoiceNetAmountAtCost =
string.IsNullOrWhiteSpace(
row.Cells[(int) TableGroupParser.InvoiceTableColumns.InvoiceNetAmountAtCost].EditedFormattedValue
.ToString())
? 0
: decimal.Parse(
row.Cells[(int) TableGroupParser.InvoiceTableColumns.InvoiceNetAmountAtCost]
.EditedFormattedValue.ToString());
invoice.InvoiceNote =
row.Cells[(int) TableGroupParser.InvoiceTableColumns.InvoiceNote].EditedFormattedValue.ToString();
invoice.FkSupplierId = supplier.Id;
idNumbers.Add(row.Index, invoice);
}
else
{
throw new TransactionAbortedException("Failed to retrieve Invoice record");
}
}
//Update the last row that was done before jumping to the next loop.
lastRow = row.Index;
}
if (lastRow != -1)
{
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
foreach (var index in idNumbers)
{
invoicesDataGridView.Rows[index.Key].Cells[(int)TableGroupParser.InvoiceTableColumns.Id].Value = index.Value.Id;
invoicesDataGridView.Rows[index.Key].HeaderCell.Style.BackColor = TableColors.EditingSaved;
invoicesDataGridView.Rows[index.Key].Cells[(int)TableGroupParser.InvoiceTableColumns.IsDirty].Value = false;
}
informationLabel.Text += @"Updated the Invoices table." + Environment.NewLine;
}
else
{
informationLabel.Text += @"No changes detected in Invoices." + Environment.NewLine;
}
scope.Complete();
}
catch (TransactionAbortedException e)
{
informationLabel.Text += @"Failed to update Invoices." + Environment.NewLine;
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
//Check for the ID numbers to see what is in the database and what isn't.
foreach (DataGridViewRow row in inventoryDataGridView.Rows)
{
if (row.IsNewRow) break;
if (row.Cells[(int) TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue.ToString() != string.Empty)
continue;
var id = int.Parse(row.Cells[(int) TableGroupParser.InvoiceTableColumns.Id].EditedFormattedValue.ToString());
if (db.Invoices.Any(x => x.Id == id)) continue;
row.Cells[(int) TableGroupParser.InvoiceTableColumns.Id].Value = string.Empty;
row.Cells[(int) TableGroupParser.InvoiceTableColumns.IsDirty].Value = true;
}
}
}
}
private void SaveWeeklySales(WeekEndingDate date)
{
if (!isWeeklySalesDirtyCheckBox.Checked)
{
informationLabel.Text += @"No changes detected to Weekly Sales." + Environment.NewLine;
return;
}
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var weeklySale = new WeeklySale();
//Check for an ID number
if (isWeeklySalesDirtyCheckBox.Tag == null)
{
//If there isn't one then create a new record and add it to the database.
weeklySale.Sunday = sundayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(sundayWeeklySalesTextBox.Text);
weeklySale.Monday = mondayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(mondayWeeklySalesTextBox.Text);
weeklySale.Tuesday = tuesdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(tuesdayWeeklySalesTextBox.Text);
weeklySale.Wednesday = wednesdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(wednesdayWeeklySalesTextBox.Text);
weeklySale.Thursday = thursdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(thursdayWeeklySalesTextBox.Text);
weeklySale.Friday = fridayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(fridayWeeklySalesTextBox.Text);
weeklySale.Saturday = saturdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(saturdayWeeklySalesTextBox.Text);
weeklySale.TotalSales = totalWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(totalWeeklySalesTextBox.Text);
weeklySale.FkDateId = date.Id;
db.WeeklySales.Add(weeklySale);
}
else
{
var id = (int) isWeeklySalesDirtyCheckBox.Tag;
weeklySale = db.WeeklySales.Single(x => x.Id == id);
//If there is one, set the current object to reference the existing record and update.
weeklySale.Sunday = sundayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(sundayWeeklySalesTextBox.Text);
weeklySale.Monday = mondayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(mondayWeeklySalesTextBox.Text);
weeklySale.Tuesday = tuesdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(tuesdayWeeklySalesTextBox.Text);
weeklySale.Wednesday = wednesdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(wednesdayWeeklySalesTextBox.Text);
weeklySale.Thursday = thursdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(thursdayWeeklySalesTextBox.Text);
weeklySale.Friday = fridayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(fridayWeeklySalesTextBox.Text);
weeklySale.Saturday = saturdayWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(saturdayWeeklySalesTextBox.Text);
weeklySale.TotalSales = totalWeeklySalesTextBox.Text == string.Empty ? 0 : decimal.Parse(totalWeeklySalesTextBox.Text);
}
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
isWeeklySalesDirtyCheckBox.Tag = weeklySale.Id;
isWeeklySalesDirtyCheckBox.Checked = false;
informationLabel.Text += @"Updated Weekly Sales." + Environment.NewLine;
scope.Complete();
}
catch (TransactionAbortedException e)
{
errorLabel.Text = @"Failed to update Weekly Sales.";
informationLabel.Text += @"Failed to update Weekly Sales." + Environment.NewLine;
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
//If the tag contains an object check to see if its in the database, if its not clear the tag out.
if (isWeeklySalesDirtyCheckBox.Tag != null)
{
var id = (int) isWeeklySalesDirtyCheckBox.Tag;
if (!db.WeeklySales.Any(x => x.Id == id))
{
isWeeklySalesDirtyCheckBox.Tag = null;
isWeeklySalesDirtyCheckBox.Checked = true;
}
}
}
}
}
private void SaveTaxable(WeekEndingDate date)
{
if (!isTaxableDirtyCheckBox.Checked)
{
informationLabel.Text += @"No changes detected to Taxable." + Environment.NewLine;
return;
}
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var taxable = new Taxable();
//Check for an ID number
if (isTaxableDirtyCheckBox.Tag == null)
{
//If there isn't one then create a new record and add it to the database.
taxable.Sunday = sundayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(sundayTaxableTextBox.Text);
taxable.Monday = mondayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(mondayTaxableTextBox.Text);
taxable.Tuesday = tuesdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(tuesdayTaxableTextBox.Text);
taxable.Wednesday = wednesdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(wednesdayTaxableTextBox.Text);
taxable.Thursday = thursdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(thursdayTaxableTextBox.Text);
taxable.Friday = fridayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(fridayTaxableTextBox.Text);
taxable.Saturday = saturdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(saturdayTaxableTextBox.Text);
taxable.Total = totalTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(totalTaxableTextBox.Text);
taxable.FkDateId = date.Id;
db.Taxables.Add(taxable);
}
else
{
var id = (int)isTaxableDirtyCheckBox.Tag;
taxable = db.Taxables.Single(x => x.Id == id);
//If there is one, set the current object to reference the existing record and update.
taxable.Sunday = sundayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(sundayTaxableTextBox.Text);
taxable.Monday = mondayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(mondayTaxableTextBox.Text);
taxable.Tuesday = tuesdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(tuesdayTaxableTextBox.Text);
taxable.Wednesday = wednesdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(wednesdayTaxableTextBox.Text);
taxable.Thursday = thursdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(thursdayTaxableTextBox.Text);
taxable.Friday = fridayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(fridayTaxableTextBox.Text);
taxable.Saturday = saturdayTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(saturdayTaxableTextBox.Text);
taxable.Total = totalTaxableTextBox.Text == string.Empty ? 0 : decimal.Parse(totalTaxableTextBox.Text);
}
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
isTaxableDirtyCheckBox.Tag = taxable.Id;
isTaxableDirtyCheckBox.Checked = false;
informationLabel.Text += @"Updated Taxable." + Environment.NewLine;
scope.Complete();
}
catch (TransactionAbortedException e)
{
errorLabel.Text = @"Failed to update Taxable.";
informationLabel.Text += @"Failed to update Taxable." + Environment.NewLine;
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
//If the tag contains an object check to see if its in the database, if its not clear the tag out.
if (isTaxableDirtyCheckBox.Tag != null)
{
var id = (int)isTaxableDirtyCheckBox.Tag;
if (!db.Taxables.Any(x => x.Id == id))
{
isTaxableDirtyCheckBox.Tag = null;
isTaxableDirtyCheckBox.Checked = true;
}
}
}
}
}
private void SaveCostAnalysis(WeekEndingDate date)
{
if (!isCostAnalysisDirtyCheckBox.Checked)
{
informationLabel.Text += @"No changes detected to Cost Analysis." + Environment.NewLine;
return;
}
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var costAnalysis = new CostAnalysi();
//Check for an ID number
if (isCostAnalysisDirtyCheckBox.Tag == null)
{
//If there isn't one then create a new record and add it to the database.
costAnalysis.SalesPerManHour = salesPerManHourTextBox.Text == string.Empty ? 0 : decimal.Parse(salesPerManHourTextBox.Text);
costAnalysis.SalaryPercentage = salaryPercentageTextBox.Text == string.Empty ? 0 : decimal.Parse(salaryPercentageTextBox.Text);
costAnalysis.SalaryDollar = salaryDollarsTextBox.Text == string.Empty ? 0 : decimal.Parse(salaryDollarsTextBox.Text);
costAnalysis.Supplies = suppliesTextBox.Text == string.Empty ? 0 : decimal.Parse(suppliesTextBox.Text);
costAnalysis.FkDateId = date.Id;
db.CostAnalysis.Add(costAnalysis);
}
else
{
var id = (int)isCostAnalysisDirtyCheckBox.Tag;
costAnalysis = db.CostAnalysis.Single(x => x.Id == id);
//If there is one, set the current object to reference the existing record and update.
costAnalysis.SalesPerManHour = salesPerManHourTextBox.Text == string.Empty ? 0 : decimal.Parse(salesPerManHourTextBox.Text);
costAnalysis.SalaryPercentage = salaryPercentageTextBox.Text == string.Empty ? 0 : decimal.Parse(salaryPercentageTextBox.Text);
costAnalysis.SalaryDollar = salaryDollarsTextBox.Text == string.Empty ? 0 : decimal.Parse(salaryDollarsTextBox.Text);
costAnalysis.Supplies = suppliesTextBox.Text == string.Empty ? 0 : decimal.Parse(suppliesTextBox.Text);
}
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
isCostAnalysisDirtyCheckBox.Tag = costAnalysis.Id;
isCostAnalysisDirtyCheckBox.Checked = false;
informationLabel.Text += @"Updated Cost Analysis." + Environment.NewLine;
scope.Complete();
}
catch (TransactionAbortedException e)
{
errorLabel.Text = @"Failed to update Cost Analysis.";
informationLabel.Text += @"Failed to update Cost Analysis." + Environment.NewLine;
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
//If the tag contains an object check to see if its in the database, if its not clear the tag out.
if (isCostAnalysisDirtyCheckBox.Tag != null)
{
var id = (int)isCostAnalysisDirtyCheckBox.Tag;
if (!db.CostAnalysis.Any(x => x.Id == id))
{
isCostAnalysisDirtyCheckBox.Tag = null;
isCostAnalysisDirtyCheckBox.Checked = true;
}
}
}
}
}
private void SaveComments(WeekEndingDate date)
{
if (!isCommentDirtyCheckBox.Checked)
{
informationLabel.Text += @"No changes detected for Comments." + Environment.NewLine;
return;
}
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var note = new Note();
//Check for an ID number
if (isCommentDirtyCheckBox.Tag == null)
{
//If there isn't one then create a new record and add it to the database.
note.Remark = commentsTextBox.Text;
note.FkDateId = date.Id;
db.Notes.Add(note);
}
else
{
//If there is one, set the current object to reference the existing record and update.
var id = (int) isCommentDirtyCheckBox.Tag;
note = db.Notes.Single(x => x.Id == id);
note.Remark = commentsTextBox.Text;
}
db.SaveChanges();
//Insert the ID numbers into the respective rows, making the assumption that all the methods will succeed.
isCommentDirtyCheckBox.Tag = note.Id;
isCommentDirtyCheckBox.Checked = false;
informationLabel.Text += @"Updated Comments." + Environment.NewLine;
scope.Complete();
}
catch (TransactionAbortedException e)
{
errorLabel.Text = @"Failed to update Comments.";
informationLabel.Text += @"Failed to update Comments." + Environment.NewLine;
if (e.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.Message);
if (e.InnerException.InnerException != null)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.InnerException.InnerException.Message);
}
}
//If the tag contains an object check to see if its in the database, if its not clear the tag out.
if (isCommentDirtyCheckBox.Tag != null)
{
var id = (int)isCommentDirtyCheckBox.Tag;
if (!db.Notes.Any(x => x.Id == id))
{
isCommentDirtyCheckBox.Tag = null;
isCommentDirtyCheckBox.Checked = true;
}
}
}
}
}
private static AdSpecial GetAdSpecial(string adSpecialText)
{
var db = new AdvertisingProfitControlDbContext();
var adSpecial = new AdSpecial();
//An ad special does exist so grab its ID from the database.
if (db.AdSpecials.Any(x => x.Name == adSpecialText))
{
//Match found
adSpecial = db.AdSpecials.Single(x => x.Name == adSpecialText);
}
else
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, "Failed to retrieve the ad special ID for '" + adSpecialText + "'.");
}
return adSpecial;
}
private static bool DeleteApcRow(int projectionId, int inventoryId, int actualSalesId)
{
var result = true;
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var projection = db.Projections.SingleOrDefault(x => x.Id == projectionId);
if (projection != null)
{
db.Projections.Remove(projection);
}
var inventory = db.Inventories.SingleOrDefault(x => x.Id == inventoryId);
if (inventory != null)
{
db.Inventories.Remove(inventory);
}
var actualSale = db.ActualSales.SingleOrDefault(x => x.Id == actualSalesId);
if (actualSale != null)
{
db.ActualSales.Remove(actualSale);
}
db.SaveChanges();
scope.Complete();
}
catch (DbUpdateException e)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.Message);
result = false;
}
}
return result;
}
private static bool DeleteInvoiceRow(int invoiceId)
{
var result = true;
var db = new AdvertisingProfitControlDbContext();
using (var scope = new TransactionScope())
{
try
{
var invoice = db.Invoices.SingleOrDefault(x => x.Id == invoiceId);
if (invoice != null)
{
db.Invoices.Remove(invoice);
}
db.SaveChanges();
scope.Complete();
}
catch (DbUpdateException e)
{
LogConsole.WriteToLog(FrmLogConsole.Level.Error, e.Message);
result = false;
}
}
return result;
}
#endregion
#region Sales Table Calculation Methods
private static void CalculateTotalPofitReturn(DataGridViewRow salesTableRow)
{
var sold = salesTableRow.Cells[(int) TableGroupParser.SalesTableColumns.Sold].EditedFormattedValue.ToString();
double profitReturn = 0;
//Check to see if the profit return cell has any values.
if (
!string.IsNullOrWhiteSpace(salesTableRow.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue.ToString
()))
{
//All validation on the contents of the cell should have been taken care of, so just parse.
profitReturn =
double.Parse(
salesTableRow.Cells[(int) TableGroupParser.SalesTableColumns.ProfitReturn].EditedFormattedValue
.ToString());
}
if (double.TryParse(sold, out double soldCount))
{
salesTableRow.Cells[(int) TableGroupParser.SalesTableColumns.TotalProfitReturn].Value = (soldCount * profitReturn).ToString("N");
}
else if (sold.Contains("in"))
{
//If the sold string contains the text "in" which would imply the word Bin or Bins is present.
//So extract the numbers from the text and calculate the total profit return.
var soldCountText = new StringBuilder();
foreach (var c in sold)
{
if (char.IsNumber(c))
{
soldCountText.Append(c);
}
else
{
//If the character isn't a number then break out of the loop, there will be no more numbers
//to worry about since the text is in the # Bin(s) format.
break;
}
}
soldCount = double.Parse(soldCountText.ToString());
salesTableRow.Cells[(int)TableGroupParser.SalesTableColumns.TotalProfitReturn].Value = (soldCount * profitReturn).ToString("N");
}
}
#endregion
private void SaveRecordOnAddRecordButtonClick(object sender, EventArgs e)
{
SaveRecords();
}
#region Menu Item Click Events
private void CheckFormStateOnClosing(object sender, FormClosingEventArgs e)
{
if (!_isFormDirty) return;
var result = MessageBox.Show(@"Would you like to save the changes you have made?", @"Save Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
switch (result)
{
case DialogResult.Yes:
SaveRecords();
break;
case DialogResult.Cancel:
e.Cancel = true;
break;
}
}
private void CreateNewRecordEditMainMenuOnClick(object sender, EventArgs e)
{
if (_isFormDirty)
{
var result = MessageBox.Show(@"Do you wish to save the changes you have made before creating a new record?", @"Save Changes", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Yes)
{
SaveRecords();
}
else if (result == DialogResult.Cancel)
{
return;
}
}
ClearFormState();
addRecordButton.Text = @"Add Record";
var db = new AdvertisingProfitControlDbContext();
var recentDate = db.WeekEndingDates.OrderByDescending(x => x.EndingDate).FirstOrDefault(x => x.EndingDate.Year == _currentActiveDate.Year);
if (recentDate == null)
{
errorLabel.Text = @"Bad things...";
return;
}
_currentActiveDate = recentDate.EndingDate.AddDays(7);
weekEndingCalendar.SelectionStart = _currentActiveDate;
Text = @"Add New Record (Current Date: " + _currentActiveDate.ToShortDateString() + @")";
informationLabel.Text = string.Empty;
errorLabel.Text = string.Empty;
_isModifyingRecord = false;
}
private void ViewLogFiles(object sender, EventArgs e)
{
var form = new FrmLogFileViewer();
form.ShowDialog();
}
private void DisplayLogConsole(object sender, EventArgs e)
{
if (LogConsole.IsVisable)
{
LogConsole.Hide();
}
else
{
LogConsole.Show();
}
}
#endregion
#region Drag and Drop Methods
//Drag and drop functionality shamelessly ripped from StackOverflow.
//Reordering rows: http://stackoverflow.com/questions/1620947/how-could-i-drag-and-drop-datagridview-rows-under-each-other/1623968#1623968
private void DataGridViewMouseMove(object sender, MouseEventArgs e)
{
var dataGridView = (DataGridView) sender;
if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
{
// If the mouse moves outside the rectangle, start the drag.
if (_dragBoxFromMouseDown != Rectangle.Empty &&
!_dragBoxFromMouseDown.Contains(e.X, e.Y))
{
// Proceed with the drag and drop, passing in the list item.
dataGridView.DoDragDrop(
dataGridView.Rows[_rowIndexFromMouseDown],
DragDropEffects.Move);
}
}
}
private void DataGridViewMouseDown(object sender, MouseEventArgs e)
{
var dataGridView = (DataGridView)sender;
// Get the index of the item the mouse is below.
_rowIndexFromMouseDown = dataGridView.HitTest(e.X, e.Y).RowIndex;
if (_rowIndexFromMouseDown != -1)
{
// Remember the point where the mouse down occurred.
// The DragSize indicates the size that the mouse can move
// before a drag event should be started.
var dragSize = SystemInformation.DragSize;
// Create a rectangle using the DragSize, with the mouse position being
// at the center of the rectangle.
_dragBoxFromMouseDown = new Rectangle(new Point(e.X - (dragSize.Width / 2),
e.Y - (dragSize.Height / 2)),
dragSize);
}
else
// Reset the rectangle if the mouse is not over an item in the ListBox.
_dragBoxFromMouseDown = Rectangle.Empty;
}
private void DataGridViewDragOver(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void DataGridViewDragDrop(object sender, DragEventArgs e)
{
//Check if the item being dropped is actually a row object.
if (sender.GetType() != typeof(DataGridView)) return;
var dataGridView = (DataGridView)sender;
// The mouse locations are relative to the screen, so they must be
// converted to client coordinates.
var clientPoint = dataGridView.PointToClient(new Point(e.X, e.Y));
// Get the row index of the item the mouse is below.
_rowIndexOfItemUnderMouseToDrop =
dataGridView.HitTest(clientPoint.X, clientPoint.Y).RowIndex;
// If the drag operation was a move then remove and insert the row.
if (e.Effect != DragDropEffects.Move) return;
var rowToMove = e.Data.GetData(
typeof(DataGridViewRow)) as DataGridViewRow;
//Check to make sure that the row to move isn't null for whatever reason.
if (rowToMove == null) return;
//Also check to make sure that the new row isn't being moved.
if (rowToMove.IsNewRow) return;
//Make sure the target and destination aren't the same.
if (rowToMove.Index == _rowIndexOfItemUnderMouseToDrop) return;
//Finally check to make sure the row isn't out of bounds.
if (_rowIndexOfItemUnderMouseToDrop < 0) return;
//Check to see if the target row is the NewRow, if so subtract one to prevent
//the NewRow from being manipulated, effectively putting the row to move before the very last row.
if (_rowIndexOfItemUnderMouseToDrop == dataGridView.RowCount - 1)
{
_rowIndexOfItemUnderMouseToDrop--;
}
//For now prevent the ad special row from being moved.
if (rowToMove.Index == _adSpecialIndex)
{
return;
}
ToggleProjectionTableEvents(false);
ToggleInventoryEvents(false);
ToggleActualSalesEvents(false);
MoveDraggedRow();
ToggleProjectionTableEvents();
ToggleInventoryEvents();
ToggleActualSalesEvents();
}
private void MoveDraggedRow()
{
//Rows to be moved
var projectionRowToMove = projectionsDataGridView.Rows[_rowIndexFromMouseDown];
var inventoryRowToMove = inventoryDataGridView.Rows[_rowIndexFromMouseDown];
var actualSalesRowToMove = actualSalesDataGridView.Rows[_rowIndexFromMouseDown];
//The ad item being moved
var adItem = projectionsDataGridView.Rows[_rowIndexFromMouseDown].Cells[(int)TableGroupParser.SalesTableColumns.AdItem].EditedFormattedValue;
//Get the smallest and biggest row numbers.
var min = _rowIndexFromMouseDown > _rowIndexOfItemUnderMouseToDrop
? _rowIndexOfItemUnderMouseToDrop
: _rowIndexFromMouseDown;
var max = _rowIndexFromMouseDown > _rowIndexOfItemUnderMouseToDrop
? _rowIndexFromMouseDown
: _rowIndexOfItemUnderMouseToDrop;
//
if(_rowIndexFromMouseDown < _adSpecialIndex && _rowIndexOfItemUnderMouseToDrop > _adSpecialIndex)
{
//Make sure that the ad special group doesn't already contain the ad item that is about be dragged into it.
if (_usedAdItems[1].Contains(adItem))
{
MessageBox.Show(@"The ad special group already contains the ad item " + adItem + @".", @"Duplicate Ad Items Not Allowed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//Move the ad item out of the regular group and into the ad special group.
_usedAdItems[0].Remove(adItem.ToString());
_usedAdItems[1].Add(adItem.ToString());
//Moving the row to move into the Ad Special group.
_adSpecialIndex--;
}
else if (_rowIndexFromMouseDown > _adSpecialIndex && _rowIndexOfItemUnderMouseToDrop < _adSpecialIndex)
{
//Make sure that section 1 doesn't already contain the ad item that is about be dragged into it.
if (_usedAdItems[0].Contains(adItem))
{
MessageBox.Show(@"The group outside of the ad special group already contains the ad item " + adItem + @".", @"Duplicate Ad Items Not Allowed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//Move the ad item out of the ad special group and into the regular group.
_usedAdItems[1].Remove(adItem.ToString());
_usedAdItems[0].Add(adItem.ToString());
//Moving the row to move OUT of the Ad Special group.
_adSpecialIndex++;
}
else if (_rowIndexOfItemUnderMouseToDrop == _adSpecialIndex)
{
//Check to see if the row from the mouse down is already in the add special group.
if (!_usedAdItems[1].Contains(adItem) && _rowIndexFromMouseDown < _adSpecialIndex)
{
//Move the ad item out of the regular and into the ad special group.
_usedAdItems[0].Remove(adItem.ToString());
_usedAdItems[1].Add(adItem.ToString());
//Move the ad special row up by one row if the user tries dropping a row on it.
_adSpecialIndex--;
}
//Make sure that the ad special group doesn't already contain the ad item that is about be dragged into it.
else if (_usedAdItems[1].Contains(adItem))
{
MessageBox.Show(@"The ad special group already contains the ad item " + adItem + @".", @"Duplicate Ad Items Not Allowed", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
projectionsDataGridView.Rows.RemoveAt(_rowIndexFromMouseDown);
projectionsDataGridView.Rows.Insert(_rowIndexOfItemUnderMouseToDrop, projectionRowToMove);
inventoryDataGridView.Rows.RemoveAt(_rowIndexFromMouseDown);
inventoryDataGridView.Rows.Insert(_rowIndexOfItemUnderMouseToDrop, inventoryRowToMove);
actualSalesDataGridView.Rows.RemoveAt(_rowIndexFromMouseDown);
actualSalesDataGridView.Rows.Insert(_rowIndexOfItemUnderMouseToDrop, actualSalesRowToMove);
MarkRowRangeForEditing(min, max);
}
private void MarkRowRangeForEditing(int startingIndex, int endingIndex)
{
//Mark the changed rows as dirty.
for (var i = startingIndex; i <= endingIndex; i++)
{
projectionsDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
inventoryDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
actualSalesDataGridView.Rows[i].HeaderCell.Value = (i + 1).ToString();
if (i == _adSpecialIndex || projectionsDataGridView.Rows[i].IsNewRow)
{
continue;
}
//Mark as dirty and update the numbers in the header cell.
projectionsDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
projectionsDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
inventoryDataGridView.Rows[i].Cells[(int)TableGroupParser.InventoryTableColumns.IsDirty].Value = true;
inventoryDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
actualSalesDataGridView.Rows[i].Cells[(int)TableGroupParser.SalesTableColumns.IsDirty].Value = true;
actualSalesDataGridView.Rows[i].HeaderCell.Style.BackColor = TableColors.PendingEdit;
}
projectionsDataGridView.Refresh();
inventoryDataGridView.Refresh();
actualSalesDataGridView.Refresh();
//Mark the form as dirty.
_isFormDirty = true;
}
#endregion
}
}