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; CurrentUserType = currentUserType;
//Sub-role rights flags //Sub-role rights flags
UserHasInventoryFullRole = CurrentUserRoles.HasFlag(AuthorizationRoles.Inventory);
UserIsTechRestricted = CurrentUserRoles.HasFlag(AuthorizationRoles.TechRestricted); UserIsTechRestricted = CurrentUserRoles.HasFlag(AuthorizationRoles.TechRestricted);
UserIsSubContractorFull = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractor); UserIsSubContractorFull = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractor);
UserIsSubContractorRestricted = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractorRestricted); UserIsSubContractorRestricted = CurrentUserType == UserType.ServiceContractor && CurrentUserRoles.HasFlag(AuthorizationRoles.SubContractorRestricted);
@@ -88,6 +89,7 @@ namespace AyaNova.Biz
internal bool UserCanViewPartCosts { get; set; } internal bool UserCanViewPartCosts { get; set; }
internal bool UserCanViewLaborOrTravelRateCosts { get; set; } internal bool UserCanViewLaborOrTravelRateCosts { get; set; }
internal bool UserCanViewLoanerCosts { get; set; } internal bool UserCanViewLoanerCosts { get; set; }
internal bool UserHasInventoryFullRole { get; set; }
@@ -145,18 +147,21 @@ namespace AyaNova.Biz
await ct.SaveChangesAsync(); await ct.SaveChangesAsync();
//INVENTORY ADJUSTMENTS //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); foreach (WorkOrderItemPart wip in wi.Parts)
if (HasErrors)
{ {
await transaction.RollbackAsync(); await PartInventoryAdjustmentAsync(AyaEvent.Created, wip, null, transaction);
return null; 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 //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.WorkOrderItemPart.AddAsync(newObject);
await ct.SaveChangesAsync(); 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) if (HasErrors)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
@@ -3750,7 +3756,8 @@ namespace AyaNova.Biz
try try
{ {
await ct.SaveChangesAsync(); 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) if (HasErrors)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
@@ -3787,7 +3794,8 @@ namespace AyaNova.Biz
await PartBizActionsAsync(AyaEvent.Deleted, null, dbObject); await PartBizActionsAsync(AyaEvent.Deleted, null, dbObject);
ct.WorkOrderItemPart.Remove(dbObject); ct.WorkOrderItemPart.Remove(dbObject);
await ct.SaveChangesAsync(); 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) if (HasErrors)
{ {
await transaction.RollbackAsync(); await transaction.RollbackAsync();
@@ -4001,64 +4009,126 @@ namespace AyaNova.Biz
{ {
PartInventoryBiz pib = new PartInventoryBiz(ct, UserId, UserTranslationId, CurrentUserRoles); PartInventoryBiz pib = new PartInventoryBiz(ct, UserId, UserTranslationId, CurrentUserRoles);
//DELETED, HANDLE INVENTORY / RETURN SERIALS //DELETED, HANDLE INVENTORY / SERIALS
if (ayaEvent == AyaEvent.Deleted && oldObj.Quantity != 0) if (ayaEvent == AyaEvent.Deleted && oldObj.Quantity != 0)
{ {
dtInternalPartInventory pi = //NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
new dtInternalPartInventory //was originally a return of inventory so needs reversal
{ if (oldObj.Quantity < 0)
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()}"); dtInternalPartInventory pi =
return; 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 else
{ //return serial numbers to part {
if (!string.IsNullOrWhiteSpace(oldObj.Serials)) //was consumed inventory so return it
await PartBiz.AppendSerialsAsync(oldObj.PartId, oldObj.Serials, ct, UserId); 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 //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 if (ayaEvent == AyaEvent.Created && newObj.Quantity != 0)//allow zero quantity parts on workorder as placeholder, serials will not be consumed
{ {
dtInternalPartInventory pi =
new dtInternalPartInventory if (newObj.Quantity < 0)
{
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) //NEGATIVE BLOCK >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
{
foreach (var e in pib.Errors) //RETURN INVENTORY
{ dtInternalPartInventory pi =
if (e.Code == ApiErrorCode.INSUFFICIENT_INVENTORY) new dtInternalPartInventory
AddError(e.Code, "Quantity", e.Message); {
else PartId = newObj.PartId,
AddError(e.Code, e.Target, e.Message); PartWarehouseId = newObj.PartWarehouseId,
} Quantity = newObj.Quantity * -1,
} SourceType = AyaType.WorkOrderItemPart,
//AddError(ApiErrorCode.API_SERVER_ERROR, "generalerror", $"Error updating inventory ({pi.Description}):{pib.GetErrorsAsString()}"); SourceId = newObj.Id,
return; Description = await Translate("WorkOrderItemPart") + $" {newObj.Serials} " + await Translate("EventCreated")
};
//<<<<<<<<<<<<<<<<<<<<<< NEGATIVE BLOCK
} }
else else
{ //Consume serial numbers from part {
if (!string.IsNullOrWhiteSpace(newObj.Serials)) //CONSUME INVENTORY
await PartBiz.RemoveSerialsAsync(newObj.PartId, newObj.Serials, ct, UserId); 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; return;
} }
if (proposedObj.Quantity < 0)//negative quantities are not allowed if (proposedObj.Quantity < 0 && !UserHasInventoryFullRole)//negative quantities are not allowed unless the user has inventory full role
AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity"); AddError(ApiErrorCode.VALIDATION_INVALID_VALUE, "Quantity", await Translate("InventoryRoleRequired"));
//Any form customizations to validate? //Any form customizations to validate?
var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.WorkOrderItemPart.ToString()); var FormCustomization = await ct.FormCustom.AsNoTracking().SingleOrDefaultAsync(z => z.FormKey == AyaType.WorkOrderItemPart.ToString());

View File

@@ -2301,5 +2301,6 @@
"ExcludeDaysOfWeek":"Wochentage ausschließen", "ExcludeDaysOfWeek":"Wochentage ausschließen",
"ErrorGenBeforeTooSmall":"Muss kleiner sein als Wiederholungsintervall", "ErrorGenBeforeTooSmall":"Muss kleiner sein als Wiederholungsintervall",
"ErrorRepeatIntervalTooSmall":"Mindestens eine Stunde", "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", "ExcludeDaysOfWeek":"Exclude days of week",
"ErrorGenBeforeTooSmall":"Must be smaller than Repeat interval", "ErrorGenBeforeTooSmall":"Must be smaller than Repeat interval",
"ErrorRepeatIntervalTooSmall":"Minimum one hour", "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", "ExcludeDaysOfWeek":"Excluir días de la semana",
"ErrorGenBeforeTooSmall":"Debe ser menor que el intervalo de repetición", "ErrorGenBeforeTooSmall":"Debe ser menor que el intervalo de repetición",
"ErrorRepeatIntervalTooSmall":"Mínimo una hora", "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", "ExcludeDaysOfWeek":"Exclure les jours de la semaine",
"ErrorGenBeforeTooSmall":"Doit être inférieur à l'intervalle de répétition", "ErrorGenBeforeTooSmall":"Doit être inférieur à l'intervalle de répétition",
"ErrorRepeatIntervalTooSmall":"Au moins une heure", "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"
} }