diff --git a/src/Domain.LinnApps/AuthorisedAction.cs b/src/Domain.LinnApps/AuthorisedAction.cs index 823c547e5..8389dea9b 100644 --- a/src/Domain.LinnApps/AuthorisedAction.cs +++ b/src/Domain.LinnApps/AuthorisedAction.cs @@ -17,5 +17,9 @@ public class AuthorisedAction public const string PurchaseOrderUpdate = "purchase-order.update"; public const string PlCreditDebitNoteClose = "purchasing.pl-debit-credit-note.close"; + + public const string PlCreditDebitNoteCancel = "purchasing.pl-debit-credit-note.close"; + + public const string PlCreditDebitNoteUpdate = "purchasing.pl-debit-credit-note.update"; } } diff --git a/src/Domain.LinnApps/PurchaseOrders/IPlCreditDebitNoteService.cs b/src/Domain.LinnApps/PurchaseOrders/IPlCreditDebitNoteService.cs index d0c6b523a..1ea801950 100644 --- a/src/Domain.LinnApps/PurchaseOrders/IPlCreditDebitNoteService.cs +++ b/src/Domain.LinnApps/PurchaseOrders/IPlCreditDebitNoteService.cs @@ -4,10 +4,21 @@ public interface IPlCreditDebitNoteService { - public PlCreditDebitNote CloseDebitNote( + public void CloseDebitNote( PlCreditDebitNote toClose, string reason, int closedBy, IEnumerable privileges); + + public void CancelDebitNote( + PlCreditDebitNote toCancel, + string reason, + int cancelledBy, + IEnumerable privileges); + + public void UpdatePlCreditDebitNote( + PlCreditDebitNote current, + PlCreditDebitNote updated, + IEnumerable privileges); } } diff --git a/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNote.cs b/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNote.cs index cf3749769..bbc7f0db1 100644 --- a/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNote.cs +++ b/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNote.cs @@ -14,12 +14,20 @@ public class PlCreditDebitNote public int OrderQty { get; set; } - public int? OriginalOrderNumber { get; set; } - public int? ReturnsOrderNumber { get; set; } + public int? ReturnsOrderLine { get; set; } + public decimal NetTotal { get; set; } + public decimal Total { get; set; } + + public decimal OrderUnitPrice { get; set; } + + public string OrderUnitOfMeasure { get; set; } + + public decimal VatTotal { get; set; } + public string Notes { get; set; } public DateTime? DateClosed { get; set; } @@ -30,8 +38,20 @@ public class PlCreditDebitNote public string ReasonClosed { get; set; } - public int? SupplierId { get; set; } - public Supplier Supplier { get; set; } + + public string SuppliersDesignation { get; set; } + + public PurchaseOrder PurchaseOrder { get; set; } + + public Currency Currency { get; set; } + + public decimal? VatRate { get; set; } + + public int? CancelledBy { get; set; } + + public DateTime? DateCancelled { get; set; } + + public string ReasonCancelled { get; set; } } } diff --git a/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNoteService.cs b/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNoteService.cs index 048c53abf..ec372a89e 100644 --- a/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNoteService.cs +++ b/src/Domain.LinnApps/PurchaseOrders/PlCreditDebitNoteService.cs @@ -15,7 +15,7 @@ public PlCreditDebitNoteService(IAuthorisationService authService) this.authService = authService; } - public PlCreditDebitNote CloseDebitNote( + public void CloseDebitNote( PlCreditDebitNote toClose, string reason, int closedBy, @@ -29,7 +29,28 @@ public PlCreditDebitNote CloseDebitNote( toClose.DateClosed = DateTime.Today; toClose.ReasonClosed = reason; toClose.ClosedBy = closedBy; - return toClose; + } + + public void CancelDebitNote(PlCreditDebitNote toCancel, string reason, int cancelledBy, IEnumerable privileges) + { + if (!this.authService.HasPermissionFor(AuthorisedAction.PlCreditDebitNoteCancel, privileges)) + { + throw new UnauthorisedActionException("You are not authorised to cancel debit notes"); + } + + toCancel.DateCancelled = DateTime.Today; + toCancel.ReasonCancelled = reason; + toCancel.CancelledBy = cancelledBy; + } + + public void UpdatePlCreditDebitNote(PlCreditDebitNote current, PlCreditDebitNote updated, IEnumerable privileges) + { + if (!this.authService.HasPermissionFor(AuthorisedAction.PlCreditDebitNoteUpdate, privileges)) + { + throw new UnauthorisedActionException("You are not authorised to update credit/debit notes"); + } + + current.Notes = updated.Notes; } } } diff --git a/src/Domain.LinnApps/PurchaseOrders/PurchaseOrder.cs b/src/Domain.LinnApps/PurchaseOrders/PurchaseOrder.cs index 6a368c6db..4b3c225cd 100644 --- a/src/Domain.LinnApps/PurchaseOrders/PurchaseOrder.cs +++ b/src/Domain.LinnApps/PurchaseOrders/PurchaseOrder.cs @@ -26,5 +26,7 @@ public class PurchaseOrder public decimal? OverbookQty { get; set; } public Currency Currency { get; set; } + + public string OrderContactName { get; set; } } } diff --git a/src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetails.cs b/src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetail.cs similarity index 85% rename from src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetails.cs rename to src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetail.cs index 25bbb8f27..5f69cd737 100644 --- a/src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetails.cs +++ b/src/Domain.LinnApps/PurchaseOrders/PurchaseOrderDetail.cs @@ -2,6 +2,8 @@ { using System.Collections.Generic; + using Linn.Purchasing.Domain.LinnApps.Parts; + public class PurchaseOrderDetail { public string Cancelled { get; set; } @@ -17,12 +19,10 @@ public class PurchaseOrderDetail public int? OurQty { get; set; } - public string PartNumber { get; set; } + public Part Part { get; set; } public IEnumerable PurchaseDeliveries { get; set; } - public PurchaseOrder PurchaseOrder { get; set; } - public string RohsCompliant { get; set; } public string SuppliersDesignation { get; set; } diff --git a/src/Domain.LinnApps/Reports/PurchaseOrdersReportService.cs b/src/Domain.LinnApps/Reports/PurchaseOrdersReportService.cs index 7cae9e4a6..c33b6cc42 100644 --- a/src/Domain.LinnApps/Reports/PurchaseOrdersReportService.cs +++ b/src/Domain.LinnApps/Reports/PurchaseOrdersReportService.cs @@ -64,7 +64,7 @@ public PurchaseOrdersReportService( public ResultsModel GetOrdersByPartReport(DateTime from, DateTime to, string partNumber, bool includeCancelled) { var purchaseOrders = this.purchaseOrderRepository.FilterBy( - x => x.Details.Any(z => z.PartNumber == partNumber) && from <= x.OrderDate && x.OrderDate < to); + x => x.Details.Any(z => z.Part.PartNumber == partNumber) && from <= x.OrderDate && x.OrderDate < to); var reportLayout = new SimpleGridLayout( this.reportingHelper, @@ -83,7 +83,7 @@ public ResultsModel GetOrdersByPartReport(DateTime from, DateTime to, string par continue; } - foreach (var orderDetail in order.Details.Where(d => d.PartNumber == partNumber)) + foreach (var orderDetail in order.Details.Where(d => d.Part.PartNumber == partNumber)) { foreach (var delivery in orderDetail.PurchaseDeliveries) { @@ -154,7 +154,7 @@ public ResultsModel GetOrdersBySupplierReport( continue; } - var part = this.partRepository.FindBy(x => x.PartNumber == orderDetail.PartNumber); + var part = this.partRepository.FindBy(x => x.PartNumber == orderDetail.Part.PartNumber); if (stockControlled != "A" && ((stockControlled == "N" && part.StockControlled != "N") || (stockControlled == "O" && part.StockControlled != "Y"))) { @@ -400,7 +400,7 @@ private static void ExtractSupplierReportDetails( values.Add( new CalculationValueModel { - RowId = currentRowId, ColumnId = "PartNo", TextDisplay = $"{orderDetail.PartNumber}" + RowId = currentRowId, ColumnId = "PartNo", TextDisplay = $"{orderDetail.Part.PartNumber}" }); values.Add( diff --git a/src/Domain.LinnApps/Reports/SpendsReportService.cs b/src/Domain.LinnApps/Reports/SpendsReportService.cs index 58152a373..568d5e0de 100644 --- a/src/Domain.LinnApps/Reports/SpendsReportService.cs +++ b/src/Domain.LinnApps/Reports/SpendsReportService.cs @@ -149,7 +149,7 @@ public ResultsModel GetSpendByPartReport(int supplierId) OrderNumber = s.OrderNumber.Value, OrderLine = s.OrderLine.Value, PartNumber = purchaseOrders.First(po => po.OrderNumber == s.OrderNumber.Value).Details - .First(x => x.Line == s.OrderLine.Value).PartNumber + .First(x => x.Line == s.OrderLine.Value).Part?.PartNumber }).ToList(); var distinctPartSpends = partSpends.DistinctBy(x => x.PartNumber).Select( diff --git a/src/Facade/ResourceBuilders/PlCreditDebitNoteResourceBuilder.cs b/src/Facade/ResourceBuilders/PlCreditDebitNoteResourceBuilder.cs index fedef116d..038b07652 100644 --- a/src/Facade/ResourceBuilders/PlCreditDebitNoteResourceBuilder.cs +++ b/src/Facade/ResourceBuilders/PlCreditDebitNoteResourceBuilder.cs @@ -1,38 +1,66 @@ namespace Linn.Purchasing.Facade.ResourceBuilders { - using System; using System.Collections.Generic; + using System.Linq; using Linn.Common.Facade; + using Linn.Common.Resources; using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; using Linn.Purchasing.Resources; public class PlCreditDebitNoteResourceBuilder : IBuilder { - public PlCreditDebitNoteResource Build(PlCreditDebitNote note, IEnumerable claims) - { - return new PlCreditDebitNoteResource + public PlCreditDebitNoteResource Build(PlCreditDebitNote note, IEnumerable claims) + { + return new PlCreditDebitNoteResource { OrderQty = note.OrderQty, PartNumber = note.PartNumber, DateClosed = note.DateClosed?.ToString("o"), - SupplierId = note.SupplierId, + SupplierId = note.Supplier.SupplierId, ClosedBy = note.ClosedBy, NetTotal = note.NetTotal, NoteNumber = note.NoteNumber, - OriginalOrderNumber = note.OriginalOrderNumber, + OriginalOrderNumber = note.PurchaseOrder?.OrderNumber, ReturnsOrderNumber = note.ReturnsOrderNumber, + ReturnsOrderLine = note.ReturnsOrderLine, Notes = note.Notes, SupplierName = note.Supplier?.Name, - DateCreated = note.DateCreated.ToShortDateString() + DateCreated = note.DateCreated.ToString("o"), + NoteType = note.NoteType, + SupplierFullAddress = note.Supplier.OrderFullAddress?.AddressString, + OrderUnitOfMeasure = note.OrderUnitOfMeasure, + OrderUnitPrice = note.OrderUnitPrice, + Total = note.Total, + VatTotal = note.VatTotal, + SuppliersDesignation = note.SuppliersDesignation, + OrderContactName = note.PurchaseOrder?.OrderContactName, + SupplierAddress = note.Supplier.OrderFullAddress?.AddressString, + Currency = note.Currency?.Name, + OrderDetails = note.PurchaseOrder?.Details + ?.Select(d => new PurchaseOrderDetailResource + { + Line = d.Line, + PartNumber = d.Part?.PartNumber, + PartDescription = d.Part?.Description + }), + VatRate = note.VatRate, + Cancelled = note.CancelledBy.HasValue, + Links = this.BuildLinks(note, claims).ToArray() }; - } + } - public string GetLocation(PlCreditDebitNote p) - { - throw new NotImplementedException(); - } + public string GetLocation(PlCreditDebitNote p) + { + return $"/purchasing/pl-credit-debit-notes/{p.NoteNumber}"; + } + + object IBuilder.Build(PlCreditDebitNote entity, IEnumerable claims) + => this.Build(entity, claims); - object IBuilder.Build(PlCreditDebitNote entity, IEnumerable claims) => this.Build(entity, claims); + private IEnumerable BuildLinks(PlCreditDebitNote model, IEnumerable claims) + { + yield return new LinkResource { Rel = "self", Href = this.GetLocation(model) }; + } } } diff --git a/src/Facade/Services/PlCreditDebitNoteFacadeService.cs b/src/Facade/Services/PlCreditDebitNoteFacadeService.cs index 330119661..11962a96c 100644 --- a/src/Facade/Services/PlCreditDebitNoteFacadeService.cs +++ b/src/Facade/Services/PlCreditDebitNoteFacadeService.cs @@ -2,6 +2,7 @@ { using System; using System.Collections.Generic; + using System.Linq; using System.Linq.Expressions; using Linn.Common.Facade; @@ -36,22 +37,37 @@ protected override void UpdateFromResource( PlCreditDebitNoteResource updateResource, IEnumerable privileges = null) { - if (updateResource.ClosedBy.HasValue && updateResource.Close.HasValue && (bool)updateResource.Close) + var enumerable = privileges?.ToList(); + if (updateResource.Who.HasValue && updateResource.Close.HasValue && (bool)updateResource.Close) { this.domainService.CloseDebitNote( entity, updateResource.ReasonClosed, - (int)updateResource.ClosedBy, - privileges); + (int)updateResource.Who, + enumerable); } - entity.Notes = updateResource.Notes; + if (updateResource.Who.HasValue && !string.IsNullOrEmpty(updateResource.ReasonCancelled)) + { + this.domainService.CloseDebitNote( + entity, + updateResource.ReasonClosed, + (int)updateResource.Who, + enumerable); + } + + this.domainService.UpdatePlCreditDebitNote( + entity, + new PlCreditDebitNote { Notes = updateResource.Notes }, + enumerable); } protected override Expression> SearchExpression( string searchTerm) { - throw new NotImplementedException(); + return x => x.NoteNumber.ToString() == searchTerm + || x.Supplier.SupplierId.ToString() == searchTerm + || x.Supplier.Name.ToUpper().Contains(searchTerm.ToUpper()); } protected override void SaveToLogTable( diff --git a/src/Persistence.LinnApps/Repositories/PlCreditDebitNoteRepository.cs b/src/Persistence.LinnApps/Repositories/PlCreditDebitNoteRepository.cs index 324299dab..5e3586d7f 100644 --- a/src/Persistence.LinnApps/Repositories/PlCreditDebitNoteRepository.cs +++ b/src/Persistence.LinnApps/Repositories/PlCreditDebitNoteRepository.cs @@ -16,9 +16,20 @@ public PlCreditDebitNoteRepository(ServiceDbContext serviceDbContext) { } - public override IQueryable FilterBy(Expression> expression) + public override PlCreditDebitNote FindById(int key) { - return base.FilterBy(expression).Include(n => n.Supplier); + return this.FindAll().Include(x => x.Supplier).ThenInclude(s => s.OrderFullAddress) + .Include(x => x.PurchaseOrder).ThenInclude(o => o.Details).ThenInclude(d => d.Part) + .Include(x => x.Currency) + .FirstOrDefault(x => x.NoteNumber == key); + } + + public override IQueryable FilterBy( + Expression> expression) + { + return base.FilterBy(expression) + .Include(n => n.Supplier) + .Include(x => x.PurchaseOrder).AsNoTracking(); } } } diff --git a/src/Persistence.LinnApps/Repositories/PurchaseOrderRepository.cs b/src/Persistence.LinnApps/Repositories/PurchaseOrderRepository.cs index ab80092a6..c4f065071 100644 --- a/src/Persistence.LinnApps/Repositories/PurchaseOrderRepository.cs +++ b/src/Persistence.LinnApps/Repositories/PurchaseOrderRepository.cs @@ -21,8 +21,9 @@ public PurchaseOrderRepository(ServiceDbContext serviceDbContext) public override IQueryable FilterBy(Expression> expression) { - return this.serviceDbContext.PurchaseOrders.Where(expression).Include(o => o.Details) - .ThenInclude(d => d.PurchaseDeliveries).Include(x => x.Supplier) + return this.serviceDbContext.PurchaseOrders.Where(expression) + .Include(o => o.Details).ThenInclude(d => d.Part) + .Include(o => o.Details).ThenInclude(d => d.PurchaseDeliveries).Include(x => x.Supplier) .Include(x => x.Currency) .AsNoTracking(); } @@ -34,9 +35,10 @@ public override IQueryable FindAll() public override PurchaseOrder FindById(int key) { - var purchaseOrder = this.serviceDbContext.PurchaseOrders.Find(key); - this.serviceDbContext.Entry(purchaseOrder).Collection(p => p.Details).Load(); - return purchaseOrder; + return this.serviceDbContext + .PurchaseOrders + .Include(o => o.Details).ThenInclude(d => d.Part) + .First(o => o.OrderNumber == key); } } } diff --git a/src/Persistence.LinnApps/ServiceDbContext.cs b/src/Persistence.LinnApps/ServiceDbContext.cs index 13a6bda4a..5eb140c01 100644 --- a/src/Persistence.LinnApps/ServiceDbContext.cs +++ b/src/Persistence.LinnApps/ServiceDbContext.cs @@ -433,6 +433,9 @@ private void BuildPurchaseOrders(ModelBuilder builder) entity.Property(o => o.Overbook).HasColumnName("OVERBOOK"); entity.Property(o => o.OverbookQty).HasColumnName("OVERBOOK_QTY"); entity.HasOne(o => o.Currency).WithMany().HasForeignKey("CURR_CODE"); + entity.Property(o => o.OrderContactName).HasColumnName("CONTACT_NAME"); + entity.Property(o => o.OrderContactName).HasColumnName("CONTACT_NAME"); + entity.HasMany(o => o.Details).WithOne().HasForeignKey(d => d.OrderNumber); } private void BuildPurchaseOrderDetails(ModelBuilder builder) @@ -444,14 +447,12 @@ private void BuildPurchaseOrderDetails(ModelBuilder builder) entity.Property(o => o.Line).HasColumnName("ORDER_LINE"); entity.Property(o => o.RohsCompliant).HasColumnName("ROHS_COMPLIANT"); entity.Property(o => o.OurQty).HasColumnName("OUR_QTY"); - entity.Property(o => o.PartNumber).HasColumnName("PART_NUMBER").HasMaxLength(14); entity.Property(o => o.SuppliersDesignation).HasColumnName("SUPPLIERS_DESIGNATION").HasMaxLength(2000); - entity.HasOne(d => d.PurchaseOrder).WithMany(o => o.Details) - .HasForeignKey(d => d.OrderNumber); entity.HasMany(d => d.PurchaseDeliveries).WithOne(o => o.PurchaseOrderDetail) .HasForeignKey(o => new { o.OrderNumber, o.OrderLine }); entity.Property(o => o.BaseNetTotal).HasColumnName("BASE_NET_TOTAL").HasMaxLength(18); entity.Property(o => o.NetTotalCurrency).HasColumnName("NET_TOTAL").HasMaxLength(18); + entity.HasOne(o => o.Part).WithMany().HasForeignKey("PART_NUMBER"); } private void BuildPurchaseOrderDeliveries(ModelBuilder builder) @@ -669,13 +670,23 @@ private void BuildPlCreditDebitNotes(ModelBuilder builder) entity.Property(a => a.DateClosed).HasColumnName("DATE_CLOSED"); entity.Property(a => a.NetTotal).HasColumnName("NET_TOTAL"); entity.Property(a => a.NoteType).HasColumnName("CDNOTE_TYPE").HasMaxLength(1); - entity.Property(a => a.OriginalOrderNumber).HasColumnName("ORIGINAL_ORDER_NUMBER"); entity.Property(a => a.ReturnsOrderNumber).HasColumnName("RETURNS_ORDER_NUMBER"); entity.Property(a => a.Notes).HasColumnName("NOTES").HasMaxLength(200); entity.Property(a => a.ReasonClosed).HasColumnName("REASON_CLOSED").HasMaxLength(2000); - entity.Property(a => a.SupplierId).HasColumnName("SUPPLIER_ID"); - entity.HasOne(a => a.Supplier).WithMany().HasForeignKey(a => a.SupplierId); + entity.HasOne(a => a.Supplier).WithMany().HasForeignKey("SUPPLIER_ID"); entity.Property(a => a.DateCreated).HasColumnName("DATE_CREATED"); + entity.Property(a => a.Total).HasColumnName("TOTAL_INC_VAT"); + entity.Property(a => a.OrderUnitPrice).HasColumnName("ORDER_UNIT_PRICE"); + entity.Property(a => a.OrderUnitOfMeasure).HasColumnName("ORDER_UNIT_OF_MEASURE"); + entity.Property(a => a.VatTotal).HasColumnName("VAT_TOTAL"); + entity.Property(a => a.SuppliersDesignation).HasColumnName("SUPPLIERS_DESIGNATION"); + entity.HasOne(a => a.PurchaseOrder).WithMany().HasForeignKey("ORIGINAL_ORDER_NUMBER"); + entity.HasOne(a => a.Currency).WithMany().HasForeignKey("CURRENCY"); + entity.Property(a => a.ReturnsOrderLine).HasColumnName("RETURNS_ORDER_LINE"); + entity.Property(a => a.VatRate).HasColumnName("VAT_RATE"); + entity.Property(a => a.CancelledBy).HasColumnName("CANCELLED_BY"); + entity.Property(a => a.DateCancelled).HasColumnName("DATE_CANCELLED"); + entity.Property(a => a.ReasonCancelled).HasColumnName("REASON_CANCELLED"); } } } diff --git a/src/Resources/PlCreditDebitNoteResource.cs b/src/Resources/PlCreditDebitNoteResource.cs index 7b2806837..397273754 100644 --- a/src/Resources/PlCreditDebitNoteResource.cs +++ b/src/Resources/PlCreditDebitNoteResource.cs @@ -2,7 +2,9 @@ { using System.Collections.Generic; - public class PlCreditDebitNoteResource + using Linn.Common.Resources; + + public class PlCreditDebitNoteResource : HypermediaResource { public int NoteNumber { get; set; } @@ -30,10 +32,38 @@ public class PlCreditDebitNoteResource public string SupplierName { get; set; } - public IEnumerable UserPrivileges { get; set; } - public string DateCreated { get; set; } public bool? Close { get; set; } + + public decimal OrderUnitPrice { get; set; } + + public string OrderUnitOfMeasure { get; set; } + + public decimal VatTotal { get; set; } + + public string SupplierFullAddress { get; set; } + + public decimal Total { get; set; } + + public string OrderContactName { get; set; } + + public string SuppliersDesignation { get; set; } + + public string SupplierAddress { get; set; } + + public int? ReturnsOrderLine { get; set; } + + public string Currency { get; set; } + + public IEnumerable OrderDetails { get; set; } + + public decimal? VatRate { get; set; } + + public bool Cancelled { get; set; } + + public int? Who { get; set; } + + public string ReasonCancelled { get; set; } } } diff --git a/src/Resources/PurchaseOrderDetailResource.cs b/src/Resources/PurchaseOrderDetailResource.cs new file mode 100644 index 000000000..9819fc0a3 --- /dev/null +++ b/src/Resources/PurchaseOrderDetailResource.cs @@ -0,0 +1,11 @@ +namespace Linn.Purchasing.Resources +{ + public class PurchaseOrderDetailResource + { + public int Line { get; set; } + + public string PartNumber { get; set; } + + public string PartDescription { get; set; } + } +} diff --git a/src/Service.Host/client/src/actions/index.js b/src/Service.Host/client/src/actions/index.js index 83df412be..ecea7478d 100644 --- a/src/Service.Host/client/src/actions/index.js +++ b/src/Service.Host/client/src/actions/index.js @@ -108,4 +108,8 @@ export const spendByPartReportActionTypes = makeActionTypes( export const plCreditDebitNoteActionTypes = makeActionTypes(itemTypes.plCreditDebitNote.actionType); +export const plCreditDebitNotesActionTypes = makeActionTypes( + itemTypes.plCreditDebitNotes.actionType +); + export const openDebitNotesActionTypes = makeActionTypes(itemTypes.openDebitNotes.actionType); diff --git a/src/Service.Host/client/src/actions/plCreditDebitNotesActions.js b/src/Service.Host/client/src/actions/plCreditDebitNotesActions.js new file mode 100644 index 000000000..dcd8be841 --- /dev/null +++ b/src/Service.Host/client/src/actions/plCreditDebitNotesActions.js @@ -0,0 +1,12 @@ +import { FetchApiActions } from '@linn-it/linn-form-components-library'; +import { plCreditDebitNotesActionTypes as actionTypes } from './index'; +import * as itemTypes from '../itemTypes'; +import config from '../config'; + +export default new FetchApiActions( + itemTypes.plCreditDebitNotes.item, + itemTypes.plCreditDebitNotes.actionType, + itemTypes.plCreditDebitNotes.uri, + actionTypes, + config.appRoot +); diff --git a/src/Service.Host/client/src/components/Root.js b/src/Service.Host/client/src/components/Root.js index f1572f72e..8da93e8be 100644 --- a/src/Service.Host/client/src/components/Root.js +++ b/src/Service.Host/client/src/components/Root.js @@ -28,6 +28,8 @@ import UnacknowledgedOrdersReport from './reports/UnacknowledgedOrdersReport'; import SpendByPartOptions from './reports/SpendByPartOptions'; import SpendByPart from './reports/SpendByPart'; import OpenDebitNotes from './plDebitCreditNotes/OpenDebitNotes'; +import Search from './plDebitCreditNotes/Search'; +import Note from './plDebitCreditNotes/Note'; const Root = ({ store }) => (
@@ -138,6 +140,16 @@ const Root = ({ store }) => ( path="/purchasing/reports/spend-by-part/report" component={SpendByPart} /> + + ({ + dialog: { + margin: theme.spacing(6), + minWidth: theme.spacing(62) + }, + total: { + float: 'right' + } + })); + const classes = useStyles(); + const dispatch = useDispatch(); + const item = useSelector(state => itemSelectorHelpers.getItem(state[plCreditDebitNote.item])); + const loading = useSelector(state => + itemSelectorHelpers.getItemLoading(state[plCreditDebitNote.item]) + ); + + const [note, setNote] = useState(null); + const [cancelDialogOpen, setCancelDialogOpen] = useState(false); + const [cancelReason, setCancelReason] = useState(''); + + const { id } = useParams(); + + useEffect(() => { + if (id) { + dispatch(plCreditDebitNoteActions.fetch(id)); + } + }, [id, dispatch]); + + useEffect(() => { + if (item) { + setNote(item); + } + }, [item]); + const Content = () => ( + + + + {`Linn ${note.noteType === 'C' ? 'Credit' : 'Debit'} Note ${note.noteNumber}`} + + + {note.cancelled && ( + + + CANCELLED + + + )} + + Supplier: + + + {note.supplierName} + + + Date: + + + + {new Date(note.dateCreated)?.toLocaleDateString()} + + + + Your Ref: + + + {note.orderContactName} + + + + Ret Order No: + + + {note.returnsOrderNumber} + + + Line: + + + {note.returnsOrderLine} + + + {note.orderDetails?.map(d => ( + + + Orig Order No: + + + {note.originalOrderNumber} + + + Line: + + + {d.line} + + + + Part: + + + {d.partNumber} + + + {d.partDescription} + + + ))} + + Qty: + + + + {`${note.orderQty} in ${note.orderUnitOfMeasure}`} + + + + + Unit Price: + + + {note.orderUnitPrice} + + + + Currency: + + + {note.currency} + + + Total Ex-Vat: + + + {note.netTotal} + + + + Vat Total: + + + + {`${note.vatTotal} at ${note.vatRate}%`} + + + + + Total Value: + + + {note.total} + + + + Notes: + + + {note.notes} + + + + {note.noteType === 'D' && ( + <> + + + THESE ITEMS ARE RETURNED FOR CREDIT + + DO NOT RESSUPPLY + + + + )} + + + + {`THIS IS A PURCHASE LEDGER ${note.noteType === 'D' ? 'DEBIT' : 'CREDIT'} NOTE`} + + + + ); + return ( + note && ( + <> +
+ + {loading ? : } + +
+ + +
+ setCancelDialogOpen(false)} + > + + +
+ + setCancelReason(newValue)} + /> + + + setCancelDialogOpen(false)} + cancelClick={() => setCancelDialogOpen(false)} + saveClick={() => { + dispatch( + plCreditDebitNoteActions.update(id, { + noteNumber: id, + reasonCancelled: cancelReason + }) + ); + }} + /> + +
+
+
+ + + + + + + + ) + ); +} + +export default Notes; diff --git a/src/Service.Host/client/src/components/plDebitCreditNotes/OpenDebitNotes.js b/src/Service.Host/client/src/components/plDebitCreditNotes/OpenDebitNotes.js index 1cc44446f..5c3ed4627 100644 --- a/src/Service.Host/client/src/components/plDebitCreditNotes/OpenDebitNotes.js +++ b/src/Service.Host/client/src/components/plDebitCreditNotes/OpenDebitNotes.js @@ -21,6 +21,8 @@ import { DataGrid } from '@mui/x-data-grid'; import { makeStyles } from '@mui/styles'; import plCreditDebitNoteActions from '../../actions/plCreditDebitNoteActions'; import openDebitNotesActions from '../../actions/openDebitNotesActions'; +import history from '../../history'; +import config from '../../config'; function OpenDebitNotes() { const dispatch = useDispatch(); @@ -127,7 +129,7 @@ function OpenDebitNotes() { } ]; return ( - + dispatch(plCreditDebitNoteActions.setSnackbarVisible(false))} diff --git a/src/Service.Host/client/src/components/plDebitCreditNotes/Search.js b/src/Service.Host/client/src/components/plDebitCreditNotes/Search.js new file mode 100644 index 000000000..4e73dd9f8 --- /dev/null +++ b/src/Service.Host/client/src/components/plDebitCreditNotes/Search.js @@ -0,0 +1,84 @@ +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + collectionSelectorHelpers, + CreateButton, + Page, + TypeaheadTable, + utilities +} from '@linn-it/linn-form-components-library'; +import Typography from '@mui/material/Typography'; +import Grid from '@mui/material/Grid'; + +import plCreditDebitNotesActions from '../../actions/plCreditDebitNotesActions'; +import history from '../../history'; +import config from '../../config'; + +function Search() { + const dispatch = useDispatch(); + + const search = searchTerm => dispatch(plCreditDebitNotesActions.search(searchTerm)); + const searchResults = useSelector(state => + collectionSelectorHelpers.getSearchItems( + state.plCreditDebitNotes, + 100, + 'noteNumber', + 'noteNumber', + 'supplierName' + ) + ); + const searchLoading = useSelector(state => + collectionSelectorHelpers.getSearchLoading(state.plCreditDebitNotes) + ); + + const item = useSelector(state => + collectionSelectorHelpers.getApplicationState(state.plCreditDebitNotes) + ); + + const createUrl = utilities.getHref(item, 'create'); + + const searchResultsTable = { + totalItemCount: searchResults.length, + rows: searchResults?.map((res, i) => ({ + id: res.noteNumber, + values: [ + { id: `${i}-0`, value: `${res.noteNumber}` }, + { id: `${i}-1`, value: `${res.noteType}` }, + { id: `${i}-2`, value: `${res.originalOrderNumber || ''}` }, + { id: `${i}-3`, value: `${res.supplierId || ''}` }, + { id: `${i}-4`, value: `${res.supplierName || ''}` }, + { id: `${i}-5`, value: `${res.partNumber || ''}` } + ], + links: res.links + })) + }; + + return ( + + + + PL Credit/Debit Notes Utility + + + + + + history.push(utilities.getSelfHref(newVal))} + clearSearch={() => {}} + loading={searchLoading} + debounce={1000} + minimumSearchTermLength={2} + /> + + + + ); +} + +export default Search; diff --git a/src/Service.Host/client/src/itemTypes.js b/src/Service.Host/client/src/itemTypes.js index 9f1a95972..2673e03ff 100644 --- a/src/Service.Host/client/src/itemTypes.js +++ b/src/Service.Host/client/src/itemTypes.js @@ -129,6 +129,12 @@ export const plCreditDebitNote = new ItemType( '/purchasing/pl-credit-debit-notes' ); +export const plCreditDebitNotes = new ItemType( + 'plCreditDebitNotes', + 'PL_CREDIT_DEBIT_NOTES', + '/purchasing/pl-credit-debit-notes' +); + export const openDebitNotes = new ItemType( 'plCreditDebitNotes', 'PL_CREDIT_DEBIT_NOTES', diff --git a/src/Service.Host/client/src/reducers/index.js b/src/Service.Host/client/src/reducers/index.js index 99da5b484..7ffbdd753 100644 --- a/src/Service.Host/client/src/reducers/index.js +++ b/src/Service.Host/client/src/reducers/index.js @@ -42,6 +42,7 @@ import unacknowledgedOrdersReport from './unacknowledgedOrdersReport'; import planners from './planners'; import spendByPartReport from './spendByPartReport'; import plCreditDebitNote from './plCreditDebitNote'; +import plCreditDebitNotes from './plCreditDebitNotes'; import openDebitNotes from './openDebitNotes'; const errors = fetchErrorReducer({ ...itemTypes, ...reportTypes }); @@ -70,6 +71,7 @@ const rootReducer = history => partSuppliers, planners, plCreditDebitNote, + plCreditDebitNotes, openDebitNotes, preferredSupplierChange, priceChangeReasons, diff --git a/src/Service.Host/client/src/reducers/plCreditDebitNotes.js b/src/Service.Host/client/src/reducers/plCreditDebitNotes.js new file mode 100644 index 000000000..6a835237f --- /dev/null +++ b/src/Service.Host/client/src/reducers/plCreditDebitNotes.js @@ -0,0 +1,14 @@ +import { collectionStoreFactory } from '@linn-it/linn-form-components-library'; +import { plCreditDebitNotesActionTypes as actionTypes } from '../actions'; +import * as itemTypes from '../itemTypes'; + +const defaultState = { + loading: false, + items: [] +}; + +export default collectionStoreFactory( + itemTypes.plCreditDebitNotes.actionType, + actionTypes, + defaultState +); diff --git a/src/Service/Modules/PlCreditDebitNotesModule.cs b/src/Service/Modules/PlCreditDebitNotesModule.cs index d9d1c90a0..f2f395b3f 100644 --- a/src/Service/Modules/PlCreditDebitNotesModule.cs +++ b/src/Service/Modules/PlCreditDebitNotesModule.cs @@ -23,6 +23,8 @@ public PlCreditDebitNotesModule( { this.service = service; this.Get("/purchasing/open-debit-notes", this.GetOpenDebitNotes); + this.Get("/purchasing/pl-credit-debit-notes", this.SearchNotes); + this.Get("/purchasing/pl-credit-debit-notes/{id}", this.GetNote); this.Put("/purchasing/pl-credit-debit-notes/{id}", this.UpdateDebitNote); } @@ -37,10 +39,19 @@ private async Task GetOpenDebitNotes(HttpRequest req, HttpResponse res) await res.Negotiate(results); } + private async Task SearchNotes(HttpRequest req, HttpResponse res) + { + var search = req.Query.As("searchTerm"); + + var results = this.service.Search(search); + + await res.Negotiate(results); + } + private async Task UpdateDebitNote(HttpRequest req, HttpResponse res) { var resource = await req.Bind(); - resource.ClosedBy = req.HttpContext.User.GetEmployeeNumber(); + resource.Who = req.HttpContext.User.GetEmployeeNumber(); var result = this.service.Update( req.RouteValues.As("id"), resource, @@ -48,5 +59,13 @@ private async Task UpdateDebitNote(HttpRequest req, HttpResponse res) await res.Negotiate(result); } + + private async Task GetNote(HttpRequest req, HttpResponse res) + { + var result = this.service.GetById( + req.RouteValues.As("id")); + + await res.Negotiate(result); + } } } diff --git a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenClosing.cs b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenClosing.cs index 11020eb06..7b744da76 100644 --- a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenClosing.cs +++ b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenClosing.cs @@ -7,6 +7,7 @@ using FluentAssertions; using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + using Linn.Purchasing.Domain.LinnApps.Suppliers; using Linn.Purchasing.Integration.Tests.Extensions; using Linn.Purchasing.Resources; @@ -20,8 +21,6 @@ public class WhenClosing : ContextBase private PlCreditDebitNote note; - private PlCreditDebitNote closed; - [SetUp] public void SetUp() { @@ -35,25 +34,12 @@ public void SetUp() this.note = new PlCreditDebitNote { NoteNumber = this.resource.NoteNumber, - DateCreated = DateTime.UnixEpoch + DateCreated = DateTime.UnixEpoch, + Supplier = new Supplier { SupplierId = 1 } }; - this.closed = new PlCreditDebitNote - { - NoteNumber = this.resource.NoteNumber, - DateClosed = DateTime.Today, - ReasonClosed = this.resource.ReasonClosed, - DateCreated = DateTime.UnixEpoch - }; - this.MockPlCreditDebitNoteRepository.FindById(1).Returns(this.note); - this.MockDomainService.CloseDebitNote( - this.note, - this.resource.ReasonClosed, - (int)this.resource.ClosedBy, - Arg.Any>()) - .Returns(this.closed); this.Response = this.Client.Put( $"/purchasing/pl-credit-debit-notes/{this.resource.NoteNumber}", this.resource, diff --git a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingById.cs b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingById.cs new file mode 100644 index 000000000..7248eaf0e --- /dev/null +++ b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingById.cs @@ -0,0 +1,77 @@ +namespace Linn.Purchasing.Integration.Tests.PlCreditDebitNotesModuleTests +{ + using System; + using System.Net; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + using Linn.Purchasing.Domain.LinnApps.Suppliers; + using Linn.Purchasing.Integration.Tests.Extensions; + using Linn.Purchasing.Resources; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenGettingById : ContextBase + { + private PlCreditDebitNote data; + [SetUp] + public void SetUp() + { + this.data = new PlCreditDebitNote + { + NoteNumber = 1, + Supplier = new Supplier { Name = "SUPPLIER", SupplierId = 123 }, + PartNumber = "PART", + DateCreated = DateTime.UnixEpoch, + NetTotal = 100, + NoteType = "C", + Notes = "NOTES", + OrderQty = 456, + PurchaseOrder = new PurchaseOrder { OrderNumber = 4567 }, + ReturnsOrderNumber = 4321 + }; + this.MockPlCreditDebitNoteRepository.FindById(this.data.NoteNumber).Returns(this.data); + + this.Response = this.Client.Get( + $"/purchasing/pl-credit-debit-notes/{this.data.NoteNumber}", + with => + { + with.Accept("application/json"); + }).Result; + } + + [Test] + public void ShouldReturnOk() + { + this.Response.StatusCode.Should().Be(HttpStatusCode.OK); + } + + [Test] + public void ShouldReturnJsonContentType() + { + this.Response.Content.Headers.ContentType.Should().NotBeNull(); + this.Response.Content.Headers.ContentType?.ToString().Should().Be("application/json"); + } + + [Test] + public void ShouldReturnJsonBody() + { + var resource = this.Response.DeserializeBody(); + resource.Should().NotBeNull(); + resource.NoteNumber.Should().Be(this.data.NoteNumber); + resource.SupplierName.Should().Be(this.data.Supplier.Name); + resource.SupplierId.Should().Be(this.data.Supplier.SupplierId); + resource.PartNumber.Should().Be(this.data.PartNumber); + resource.DateCreated.Should().Be(DateTime.UnixEpoch.ToString("o")); + resource.NetTotal.Should().Be(this.data.NetTotal); + resource.NoteType.Should().Be(this.data.NoteType); + resource.Notes.Should().Be(this.data.Notes); + resource.OrderQty.Should().Be(this.data.OrderQty); + resource.OriginalOrderNumber.Should().Be(this.data.PurchaseOrder.OrderNumber); + resource.ReturnsOrderNumber.Should().Be(this.data.ReturnsOrderNumber); + } + } +} diff --git a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingAll.cs b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingOpenDebitNotes.cs similarity index 89% rename from tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingAll.cs rename to tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingOpenDebitNotes.cs index fe04b09ec..c6e190488 100644 --- a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingAll.cs +++ b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenGettingOpenDebitNotes.cs @@ -9,6 +9,7 @@ using FluentAssertions; using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + using Linn.Purchasing.Domain.LinnApps.Suppliers; using Linn.Purchasing.Integration.Tests.Extensions; using Linn.Purchasing.Resources; @@ -16,7 +17,7 @@ using NUnit.Framework; - public class WhenGettingAll : ContextBase + public class WhenGettingOpenDebitNotes : ContextBase { [SetUp] public void SetUp() @@ -24,8 +25,8 @@ public void SetUp() this.MockPlCreditDebitNoteRepository.FilterBy(Arg.Any>>()) .Returns(new List { - new PlCreditDebitNote { NoteNumber = 1 }, - new PlCreditDebitNote { NoteNumber = 2 }, + new PlCreditDebitNote { NoteNumber = 1, Supplier = new Supplier { SupplierId = 1 } }, + new PlCreditDebitNote { NoteNumber = 2, Supplier = new Supplier { SupplierId = 2 } } }.AsQueryable()); this.Response = this.Client.Get( "/purchasing/open-debit-notes", diff --git a/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenSearching.cs b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenSearching.cs new file mode 100644 index 000000000..917eac1b9 --- /dev/null +++ b/tests/Integration/Integration.Tests/PlCreditDebitNotesModuleTests/WhenSearching.cs @@ -0,0 +1,74 @@ +namespace Linn.Purchasing.Integration.Tests.PlCreditDebitNotesModuleTests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Linq.Expressions; + using System.Net; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + using Linn.Purchasing.Domain.LinnApps.Suppliers; + using Linn.Purchasing.Integration.Tests.Extensions; + using Linn.Purchasing.Resources; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenSearching : ContextBase + { + [SetUp] + public void SetUp() + { + this.MockPlCreditDebitNoteRepository.FilterBy(Arg.Any>>()) + .Returns(new List + { + new PlCreditDebitNote + { + NoteNumber = 1, + Supplier = new Supplier { Name = "SUPPLIER" } + }, + new PlCreditDebitNote + { + NoteNumber = 2, + Supplier = new Supplier { Name = "SUPPLIER 2" } + } + }.AsQueryable()); + this.Response = this.Client.Get( + "/purchasing/pl-credit-debit-notes?searchTerm=supplier", + with => + { + with.Accept("application/json"); + }).Result; + } + + [Test] + public void ShouldReturnOk() + { + this.Response.StatusCode.Should().Be(HttpStatusCode.OK); + } + + [Test] + public void ShouldReturnJsonContentType() + { + this.Response.Content.Headers.ContentType.Should().NotBeNull(); + this.Response.Content.Headers.ContentType?.ToString().Should().Be("application/json"); + } + + [Test] + public void ShouldReturnJsonBody() + { + var resources = this.Response.DeserializeBody>()?.ToArray(); + resources.Should().NotBeNull(); + resources.Should().HaveCount(2); + resources.Should().Contain(a => a.NoteNumber == 1); + resources.Should().Contain(a => a.NoteNumber == 2); + resources.First().Links.First(l => l.Rel == "self").Href + .Should().Be("/purchasing/pl-credit-debit-notes/1"); + resources.Last().Links.First(l => l.Rel == "self").Href + .Should().Be("/purchasing/pl-credit-debit-notes/2"); + } + } +} diff --git a/tests/Unit/Domain.LinnApps.Tests/Domain.LinnApps.Tests.csproj b/tests/Unit/Domain.LinnApps.Tests/Domain.LinnApps.Tests.csproj index d8694a8c9..482fedf41 100644 --- a/tests/Unit/Domain.LinnApps.Tests/Domain.LinnApps.Tests.csproj +++ b/tests/Unit/Domain.LinnApps.Tests/Domain.LinnApps.Tests.csproj @@ -20,8 +20,4 @@ - - - - diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNote.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNote.cs new file mode 100644 index 000000000..866cbb82c --- /dev/null +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNote.cs @@ -0,0 +1,42 @@ +namespace Linn.Purchasing.Domain.LinnApps.Tests.PlCreditDebitNotesTests +{ + using System; + using System.Collections.Generic; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenCancellingADebitNote : ContextBase + { + private PlCreditDebitNote note; + + [SetUp] + public void SetUp() + { + this.note = new PlCreditDebitNote { DateCreated = DateTime.UnixEpoch, NoteNumber = 1 }; + this.MockAuthService.HasPermissionFor( + AuthorisedAction.PlCreditDebitNoteCancel, + Arg.Is>(x => x.Contains(AuthorisedAction.PlCreditDebitNoteCancel))).Returns(true); + + this.Sut.CancelDebitNote( + this.note, + "REASON", + 33087, + new List { AuthorisedAction.PlCreditDebitNoteCancel }); + } + + [Test] + public void ShouldReturnCancelled() + { + this.note.NoteNumber.Should().Be(1); + this.note.DateCancelled.Should().Be(DateTime.Today); + this.note.ReasonCancelled.Should().Be("REASON"); + this.note.CancelledBy.Should().Be(33087); + } + } +} diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNoteAndUserNotAuthorised.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNoteAndUserNotAuthorised.cs new file mode 100644 index 000000000..c5e9e37a5 --- /dev/null +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenCancellingADebitNoteAndUserNotAuthorised.cs @@ -0,0 +1,42 @@ +namespace Linn.Purchasing.Domain.LinnApps.Tests.PlCreditDebitNotesTests +{ + using System; + using System.Collections.Generic; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.Exceptions; + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenCancellingADebitNoteAndUserNotAuthorised : ContextBase + { + private Action action; + + private PlCreditDebitNote note; + + [SetUp] + public void SetUp() + { + this.note = new PlCreditDebitNote { DateCreated = DateTime.UnixEpoch, NoteNumber = 1 }; + this.MockAuthService.HasPermissionFor( + AuthorisedAction.PlCreditDebitNoteCancel, + Arg.Is>(x => !x.Contains(AuthorisedAction.PlCreditDebitNoteClose))).Returns(false); + + this.action = () => this.Sut.CancelDebitNote( + this.note, + "REASON", + 33087, + new List()); + } + + [Test] + public void ShouldThrowUnauthorisedActionException() + { + this.action.Should().Throw(); + } + } +} diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNote.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNote.cs index 58cd20123..5d29cf908 100644 --- a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNote.cs +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNote.cs @@ -15,8 +15,6 @@ public class WhenClosingADebitNote : ContextBase { private PlCreditDebitNote note; - private PlCreditDebitNote result; - [SetUp] public void SetUp() { @@ -25,7 +23,7 @@ public void SetUp() AuthorisedAction.PlCreditDebitNoteClose, Arg.Is>(x => x.Contains(AuthorisedAction.PlCreditDebitNoteClose))).Returns(true); - this.result = this.Sut.CloseDebitNote( + this.Sut.CloseDebitNote( this.note, "REASON", 33087, @@ -35,10 +33,10 @@ public void SetUp() [Test] public void ShouldReturnClosed() { - this.result.NoteNumber.Should().Be(1); - this.result.DateClosed.Should().Be(DateTime.Today); - this.result.ReasonClosed.Should().Be("REASON"); - this.result.ClosedBy.Should().Be(33087); + this.note.NoteNumber.Should().Be(1); + this.note.DateClosed.Should().Be(DateTime.Today); + this.note.ReasonClosed.Should().Be("REASON"); + this.note.ClosedBy.Should().Be(33087); } } } diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNoteAndUserNotAuthorised.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNoteAndUserNotAuthorised.cs index 57894dd41..af7f8a5d7 100644 --- a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNoteAndUserNotAuthorised.cs +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenClosingADebitNoteAndUserNotAuthorised.cs @@ -24,13 +24,13 @@ public void SetUp() this.note = new PlCreditDebitNote { DateCreated = DateTime.UnixEpoch, NoteNumber = 1 }; this.MockAuthService.HasPermissionFor( AuthorisedAction.PlCreditDebitNoteClose, - Arg.Is>(x => x.Contains(AuthorisedAction.PlCreditDebitNoteClose))).Returns(false); + Arg.Is>(x => !x.Contains(AuthorisedAction.PlCreditDebitNoteClose))).Returns(false); this.action = () => this.Sut.CloseDebitNote( this.note, "REASON", 33087, - new List { AuthorisedAction.PlCreditDebitNoteClose }); + new List()); } [Test] diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANote.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANote.cs new file mode 100644 index 000000000..f5eba4d03 --- /dev/null +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANote.cs @@ -0,0 +1,43 @@ +namespace Linn.Purchasing.Domain.LinnApps.Tests.PlCreditDebitNotesTests +{ + using System; + using System.Collections.Generic; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenUpdatingANote : ContextBase + { + private PlCreditDebitNote note; + + private PlCreditDebitNote updated; + + [SetUp] + public void SetUp() + { + this.note = new PlCreditDebitNote { DateCreated = DateTime.UnixEpoch, NoteNumber = 1 }; + this.updated = new PlCreditDebitNote { Notes = "NOTES" }; + + this.MockAuthService.HasPermissionFor( + AuthorisedAction.PlCreditDebitNoteUpdate, + Arg.Is>(x => x.Contains(AuthorisedAction.PlCreditDebitNoteUpdate))).Returns(true); + + this.Sut.UpdatePlCreditDebitNote( + this.note, + this.updated, + new List { AuthorisedAction.PlCreditDebitNoteUpdate }); + } + + [Test] + public void ShouldReturnClosed() + { + this.note.NoteNumber.Should().Be(this.note.NoteNumber); + this.note.Notes.Should().Be(this.updated.Notes); + } + } +} diff --git a/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANoteAndUserNotAuthorised.cs b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANoteAndUserNotAuthorised.cs new file mode 100644 index 000000000..358846a5d --- /dev/null +++ b/tests/Unit/Domain.LinnApps.Tests/PlCreditDebitNotesTests/WhenUpdatingANoteAndUserNotAuthorised.cs @@ -0,0 +1,41 @@ +namespace Linn.Purchasing.Domain.LinnApps.Tests.PlCreditDebitNotesTests +{ + using System; + using System.Collections.Generic; + + using FluentAssertions; + + using Linn.Purchasing.Domain.LinnApps.Exceptions; + using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; + + using NSubstitute; + + using NUnit.Framework; + + public class WhenUpdatingANoteAndUserNotAuthorised : ContextBase + { + private Action action; + + private PlCreditDebitNote note; + + [SetUp] + public void SetUp() + { + this.note = new PlCreditDebitNote { DateCreated = DateTime.UnixEpoch, NoteNumber = 1 }; + this.MockAuthService.HasPermissionFor( + AuthorisedAction.PlCreditDebitNoteUpdate, + Arg.Is>(x => !x.Contains(AuthorisedAction.PlCreditDebitNoteUpdate))).Returns(false); + + this.action = () => this.Sut.UpdatePlCreditDebitNote( + this.note, + new PlCreditDebitNote(), + new List()); + } + + [Test] + public void ShouldThrowUnauthorisedActionException() + { + this.action.Should().Throw(); + } + } +} diff --git a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsByPartReport.cs b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsByPartReport.cs index d07d680b4..599057be0 100644 --- a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsByPartReport.cs +++ b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsByPartReport.cs @@ -9,6 +9,7 @@ using FluentAssertions.Extensions; using Linn.Common.Reporting.Models; + using Linn.Purchasing.Domain.LinnApps.Parts; using Linn.Purchasing.Domain.LinnApps.PurchaseLedger; using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; using Linn.Purchasing.Domain.LinnApps.Suppliers; @@ -49,7 +50,7 @@ public void SetUp() NetTotalCurrency = 17.23m, OrderNumber = this.orderNumber, OurQty = 3, - PartNumber = this.partNumber, + Part = new Part { PartNumber = this.partNumber }, PurchaseDeliveries = new List { diff --git a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReport.cs b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReport.cs index 500fca3d3..da509660d 100644 --- a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReport.cs +++ b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReport.cs @@ -8,6 +8,7 @@ using FluentAssertions; using Linn.Common.Reporting.Models; + using Linn.Purchasing.Domain.LinnApps.Parts; using Linn.Purchasing.Domain.LinnApps.PurchaseLedger; using Linn.Purchasing.Domain.LinnApps.PurchaseOrders; using Linn.Purchasing.Domain.LinnApps.Suppliers; @@ -46,7 +47,7 @@ public void SetUp() NetTotalCurrency = 17.34m, OrderNumber = this.orderNumber, OurQty = 3, - PartNumber = this.partNumber, + Part = new Part { PartNumber = this.partNumber }, PurchaseDeliveries = new List { diff --git a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReportStockN.cs b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReportStockN.cs index 7e4081c2d..b277b92c0 100644 --- a/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReportStockN.cs +++ b/tests/Unit/Domain.LinnApps.Tests/PurchaseOrdersReportServiceTests/WhenGettingOrdsBySupplierReportStockN.cs @@ -47,7 +47,8 @@ public void SetUp() BaseNetTotal = 4m, OrderNumber = this.orderNumber, OurQty = 3, - PartNumber = this.partNumber, + Part = new Part { PartNumber = this.partNumber }, + PurchaseDeliveries = new List { @@ -85,7 +86,8 @@ public void SetUp() BaseNetTotal = 2.5m, OrderNumber = 1111, OurQty = 3, - PartNumber = "part2", + Part = new Part { PartNumber = "part2" }, + PurchaseDeliveries = new List { diff --git a/tests/Unit/Domain.LinnApps.Tests/SpendsReportServiceTests/WhenGettingPartReport.cs b/tests/Unit/Domain.LinnApps.Tests/SpendsReportServiceTests/WhenGettingPartReport.cs index 0fb265b1a..959b6c52a 100644 --- a/tests/Unit/Domain.LinnApps.Tests/SpendsReportServiceTests/WhenGettingPartReport.cs +++ b/tests/Unit/Domain.LinnApps.Tests/SpendsReportServiceTests/WhenGettingPartReport.cs @@ -75,7 +75,7 @@ public void SetUp() { Line = 1, OrderNumber = this.orderNumber1, - PartNumber = "MCAS WAN" + Part = new Part { PartNumber = "MCAS WAN" } } } }, @@ -89,7 +89,8 @@ public void SetUp() { Line = 1, OrderNumber = this.orderNumber2, - PartNumber = "MCAS 222" + Part = new Part { PartNumber = "MCAS 222" } + } } }, @@ -103,7 +104,7 @@ public void SetUp() { Line = 1, OrderNumber = this.orderNumber3, - PartNumber = "RAW 33" + Part = new Part { PartNumber = "RAW 33" } } } }