This commit is contained in:
2021-08-23 17:51:22 +00:00
parent e3423e4a37
commit 155be2f03f
5 changed files with 133 additions and 59 deletions

View File

@@ -26,6 +26,7 @@ namespace AyaNova.Biz
CurrentUserType = currentUserType;
//Sub-role rights flags
UserHasInventoryFullRole = CurrentUserRoles.HasFlag(AuthorizationRoles.Inventory);
UserIsTechRestricted = CurrentUserRoles.HasFlag(AuthorizationRoles.TechRestricted);
UserIsSubContractorFull = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractor);
UserIsSubContractorRestricted = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractorRestricted);
@@ -88,6 +89,7 @@ namespace AyaNova.Biz
internal bool UserCanViewPartCosts { get; set; }
internal bool UserCanViewLaborOrTravelRateCosts { get; set; }
internal bool UserCanViewLoanerCosts { get; set; }
internal bool UserHasInventoryFullRole { get; set; }
@@ -145,18 +147,21 @@ namespace AyaNova.Biz
await ct.SaveChangesAsync();
//INVENTORY ADJUSTMENTS
foreach (WorkOrderItem wi in newObject.Items)
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
{
foreach (WorkOrderItemPart wip in wi.Parts)
foreach (WorkOrderItem wi in newObject.Items)
{
await PartInventoryAdjustmentAsync(AyaEvent.Created, wip, null, transaction);
if (HasErrors)
foreach (WorkOrderItemPart wip in wi.Parts)
{
await transaction.RollbackAsync();
return null;
await PartInventoryAdjustmentAsync(AyaEvent.Created, wip, null, transaction);
if (HasErrors)
{
await transaction.RollbackAsync();
return null;
}
}
}
}
}
//NOTE: not running individual notification here for children, seeder won't require it and that's all that posts an entire wo currently
@@ -3694,7 +3699,8 @@ namespace AyaNova.Biz
await ct.WorkOrderItemPart.AddAsync(newObject);
await ct.SaveChangesAsync();
await PartInventoryAdjustmentAsync(AyaEvent.Created, newObject, null, transaction);
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
await PartInventoryAdjustmentAsync(AyaEvent.Created, newObject, null, transaction);
if (HasErrors)
{
await transaction.RollbackAsync();
@@ -3750,7 +3756,8 @@ namespace AyaNova.Biz
try
{
await ct.SaveChangesAsync();
await PartInventoryAdjustmentAsync(AyaEvent.Modified, putObject, dbObject, transaction);
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
await PartInventoryAdjustmentAsync(AyaEvent.Modified, putObject, dbObject, transaction);
if (HasErrors)
{
await transaction.RollbackAsync();
@@ -3787,7 +3794,8 @@ namespace AyaNova.Biz
await PartBizActionsAsync(AyaEvent.Deleted, null, dbObject);
ct.WorkOrderItemPart.Remove(dbObject);
await ct.SaveChangesAsync();
await PartInventoryAdjustmentAsync(AyaEvent.Deleted, null, dbObject, transaction);
if (AyaNova.Util.ServerGlobalBizSettings.Cache.UseInventory)
await PartInventoryAdjustmentAsync(AyaEvent.Deleted, null, dbObject, transaction);
if (HasErrors)
{
await transaction.RollbackAsync();
@@ -4001,64 +4009,126 @@ namespace AyaNova.Biz
{
PartInventoryBiz pib = new PartInventoryBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
//DELETED, HANDLE INVENTORY / RETURN SERIALS
//DELETED, HANDLE INVENTORY / SERIALS
if (ayaEvent == AyaEvent.Deleted && oldObj.Quantity != 0)
{
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = oldObj.PartId,
PartWarehouseId = oldObj.PartWarehouseId,
Quantity = oldObj.Quantity,
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
SourceId = null,//''
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
};
if (await pib.CreateAsync(pi) == null)
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//was originally a return of inventory so needs reversal
if (oldObj.Quantity < 0)
{
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}");
return;
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = oldObj.PartId,
PartWarehouseId = oldObj.PartWarehouseId,
Quantity = oldObj.Quantity * -1,//was originally returned so needs to be removed
SourceType = AyaType.WorkOrderItemPart,
SourceId = oldObj.Id,
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
};
if (await pib.CreateAsync(pi) == null)
{
if (pib.HasErrors)
{
foreach (var e in pib.Errors)
{
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
AddError(e.Code, "Quantity", e.Message);
else
AddError(e.Code, e.Target, e.Message);
}
}
return;
}
else
{ //Consume serial numbers from part
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
await PartBiz.RemoveSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
}
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
}
else
{ //return serial numbers to part
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
{
//was consumed inventory so return it
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = oldObj.PartId,
PartWarehouseId = oldObj.PartWarehouseId,
Quantity = oldObj.Quantity,
SourceType = null,//null because the po no longer exists so this is technically a manual adjustment
SourceId = null,//''
Description = await Translate("WorkOrderItemPart") + $" {oldObj.Serials} " + await Translate("EventDeleted")
};
if (await pib.CreateAsync(pi) == null)
{
AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}");
return;
}
else
{ //return serial numbers to part
if (!string.IsNullOrWhiteSpace(oldObj.Serials))
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId);
}
}
}
//CREATED, HANDLE INVENTORY / CONSUME SERIALS
if (ayaEvent == AyaEvent.Created && newObj.Quantity != 0)//allow zero quantity parts on workorder as placeholder, serials will not be consumed
{
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = newObj.PartId,
PartWarehouseId = newObj.PartWarehouseId,
Quantity = newObj.Quantity * -1,
SourceType = AyaType.WorkOrderItemPart,
SourceId = newObj.Id,
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
};
if (await pib.CreateAsync(pi) == null)
if (newObj.Quantity < 0)
{
if (pib.HasErrors)
{
foreach (var e in pib.Errors)
{
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
AddError(e.Code, "Quantity", e.Message);
else
AddError(e.Code, e.Target, e.Message);
}
}
//AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}");
return;
//NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//RETURN INVENTORY
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = newObj.PartId,
PartWarehouseId = newObj.PartWarehouseId,
Quantity = newObj.Quantity * -1,
SourceType = AyaType.WorkOrderItemPart,
SourceId = newObj.Id,
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
};
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
}
else
{ //Consume serial numbers from part
if (!string.IsNullOrWhiteSpace(newObj.Serials))
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
{
//CONSUME INVENTORY
dtInternalPartInventory pi =
new dtInternalPartInventory
{
PartId = newObj.PartId,
PartWarehouseId = newObj.PartWarehouseId,
Quantity = newObj.Quantity * -1,
SourceType = AyaType.WorkOrderItemPart,
SourceId = newObj.Id,
Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
};
if (await pib.CreateAsync(pi) == null)
{
if (pib.HasErrors)
{
foreach (var e in pib.Errors)
{
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY)
AddError(e.Code, "Quantity", e.Message);
else
AddError(e.Code, e.Target, e.Message);
}
}
return;
}
else
{ //Consume serial numbers from part
if (!string.IsNullOrWhiteSpace(newObj.Serials))
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId);
}
}
}
@@ -4183,8 +4253,8 @@ namespace AyaNova.Biz
return;
}
if (proposedObj.Quantity < 0)//negative quantities are not allowed
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity");
if (proposedObj.Quantity < 0 && !UserHasInventoryFullRole)//negative quantities are not allowed unless the user has inventory full role
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity", await Translate("InventoryRoleRequired"));
//Any form customizations to validate?
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.WorkOrderItemPart.ToString());

View File

@@ -2301,5 +2301,6 @@
"ExcludeDaysOfWeek":"Wochentage ausschließen",
"ErrorGenBeforeTooSmall":"Muss kleiner sein als Wiederholungsintervall",
"ErrorRepeatIntervalTooSmall":"Mindestens eine Stunde",
"QuantityRequired":"Erforderliche Menge"
"QuantityRequired":"Erforderliche Menge",
"InventoryRoleRequired":"Der Benutzer muss für diesen Vorgang eine Inventarrolle haben"
}

View File

@@ -2301,6 +2301,7 @@
"ExcludeDaysOfWeek":"Exclude days of week",
"ErrorGenBeforeTooSmall":"Must be smaller than Repeat interval",
"ErrorRepeatIntervalTooSmall":"Minimum one hour",
"QuantityRequired":"Quantity required"
"QuantityRequired":"Quantity required",
"InventoryRoleRequired":"User must have Inventory Role for this operation"
}

View File

@@ -2301,5 +2301,6 @@
"ExcludeDaysOfWeek":"Excluir días de la semana",
"ErrorGenBeforeTooSmall":"Debe ser menor que el intervalo de repetición",
"ErrorRepeatIntervalTooSmall":"Mínimo una hora",
"QuantityRequired":"Cantidad requerida"
"QuantityRequired":"Cantidad requerida",
"InventoryRoleRequired":"El usuario debe tener un rol de inventario para esta operación"
}

View File

@@ -2301,5 +2301,6 @@
"ExcludeDaysOfWeek":"Exclure les jours de la semaine",
"ErrorGenBeforeTooSmall":"Doit être inférieur à l'intervalle de répétition",
"ErrorRepeatIntervalTooSmall":"Au moins une heure",
"QuantityRequired":"Quantité Nécessaire"
"QuantityRequired":"Quantité Nécessaire",
"InventoryRoleRequired":"L'utilisateur doit avoir un rôle d'inventaire pour cette opération"
}