From 0265efbc265f7027d090204b9ad283e28a40dfb0 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Mon, 22 Nov 2021 20:30:03 +0000 Subject: [PATCH] case 4025 replaced partnumber with partname and changed partname to partdescription --- devdocs/specs/core-search.txt | 2 +- .../ayanova/docs/ay-biz-admin-import-v7.md | 163 +++++++++------- .../ayanova/docs/ay-start-changes-from-v7.md | 176 ++++++++++-------- server/AyaNova/DataList/PMItemPartDataList.cs | 2 +- server/AyaNova/DataList/PartDataList.cs | 28 +-- .../AyaNova/DataList/PartInventoryDataList.cs | 30 +-- .../DataList/PartInventoryRequestDataList.cs | 24 +-- .../PartInventoryTransactionsDataList.cs | 14 +- .../AyaNova/DataList/PartRestockDataList.cs | 30 +-- .../AyaNova/DataList/QuoteItemPartDataList.cs | 2 +- .../DataList/WorkOrderItemPartDataList.cs | 2 +- .../WorkOrderItemPartRequestDataList.cs | 2 +- server/AyaNova/PickList/PartPickList.cs | 16 +- server/AyaNova/biz/DataListSavedFilterBiz.cs | 2 +- server/AyaNova/biz/FormFieldReference.cs | 1 + server/AyaNova/biz/PMBiz.cs | 8 +- server/AyaNova/biz/PartAssemblyBiz.cs | 2 +- server/AyaNova/biz/PartBiz.cs | 16 +- server/AyaNova/biz/PartInventoryBiz.cs | 2 +- .../AyaNova/biz/PartInventoryDataListBiz.cs | 12 +- .../biz/PartInventoryRequestDataListBiz.cs | 10 - server/AyaNova/biz/PurchaseOrderBiz.cs | 4 +- server/AyaNova/biz/QuoteBiz.cs | 2 +- server/AyaNova/biz/TaskGroupBiz.cs | 8 +- server/AyaNova/biz/WorkOrderBiz.cs | 5 +- server/AyaNova/models/DataListColumnView.cs | 2 +- server/AyaNova/models/DataListFilterOption.cs | 4 +- server/AyaNova/models/PMItemPart.cs | 2 +- server/AyaNova/models/Part.cs | 9 +- server/AyaNova/models/PurchaseOrderItem.cs | 5 +- server/AyaNova/models/QuoteItemPart.cs | 2 +- .../AyaNova/models/ViewPartInventoryList.cs | 11 +- .../models/ViewPartInventoryRequestList.cs | 2 +- server/AyaNova/models/ViewRestockRequired.cs | 11 +- .../models/ViewUnfulfilledPartRequestList.cs | 2 +- server/AyaNova/resource/de.json | 2 +- server/AyaNova/resource/en.json | 2 +- server/AyaNova/resource/es.json | 2 +- server/AyaNova/resource/fr.json | 2 +- ...ry Reconciliation Form Grouped By Tag.ayrt | 2 +- ...Example Inventory Reconciliation Form.ayrt | 2 +- ...Tags with optional filter by contains.ayrt | 2 +- ...e Parts with available serial numbers.ayrt | 2 +- server/AyaNova/util/AySchema.cs | 16 +- server/AyaNova/util/Seeder.cs | 18 +- 45 files changed, 336 insertions(+), 327 deletions(-) diff --git a/devdocs/specs/core-search.txt b/devdocs/specs/core-search.txt index abd31cf3..cb4992eb 100644 --- a/devdocs/specs/core-search.txt +++ b/devdocs/specs/core-search.txt @@ -44,7 +44,7 @@ REQUIREMENTS - For performance it would be something that runs *after* the search results are returned, either on demand (user clicks on excert button, or slowly fills in the list async) - NAME: in search index include a bool indicating the word is actually part of the name or equivalent of the object, this will make name searches WAAAAYYY easier!!! - - in non named objects it's whatever the primary identifier is, i.e. workorder number for a workorder, partnumber for a part + - in non named objects it's whatever the primary identifier is, i.e. workorder number for a workorder, partb number for a part - Maybe all objects should have a "name" column in db be it a workorder number or part number just for consistency - RIGHTS: if a search is performed that is not NAME ONLY then the user role must have rights to read the full object even if the results are found in the name as this simplifies things greatly - TAGS: Any search, anywhere, can include tags (internally it will post filter on tags after initial text search or if no text then will just search tags) diff --git a/docs/8.0/ayanova/docs/ay-biz-admin-import-v7.md b/docs/8.0/ayanova/docs/ay-biz-admin-import-v7.md index cfc7d347..7139143d 100644 --- a/docs/8.0/ayanova/docs/ay-biz-admin-import-v7.md +++ b/docs/8.0/ayanova/docs/ay-biz-admin-import-v7.md @@ -1,37 +1,53 @@ # BUSINESS ADMINISTRATION MIGRATE TO V8 ## OVERVIEW + This page outlines the changes and setup tasks that will need to be done after migration from a business administration point of view. For a technical guide to migration process see the [Operations migrate guide](ops-import-v7.md) ## What is migrated and where ### All objects -All migrated business objects can be tagged to easily identify them. By default it will tag all migrated objects with "v8-migrate" or you can clear this field and leave it blank if you do not wish to have all the migrated items tagged. This tag is provided so there is a record of which items were migrated and which were created after migration however there is no technical requirement for this tag; it's for informational and troubleshooting purposes only. + +All migrated business objects can be tagged to easily identify them. By default it will tag all migrated objects with "v8-migrate" or you can clear this field and leave it blank if you do not wish to have all the migrated items tagged. This tag is provided so there is a record of which items were migrated and which were created after migration however there is no technical requirement for this tag; it's for informational and troubleshooting purposes only. ### Most objects Most object will retain their name and will be migrated directly with the following exceptions: -### Global Wiki +### Global Wiki + v8 does not have a Global Wiki page (the Wiki page formerly accessible from the opening screen in AyaNova 7), the migration will move the Wiki and any embedded files to a User account created just to contain it's Wiki and attached files named "zV8Migrate GLOBAL_WIKI_REPOSITORY". ### Region Wiki + v8 does not have Regions, all functionality previously in Regions will be split out into several new and changed objects. The Region Wiki if found will be migrated to a User account created just to hold the Wiki and files named "zV8Migrate REGION_WIKI_REPOSITORY_region name" -### Security groups -* Security groups have been replaced by Roles. -* No v7 security group information or settings are migrated into v8. -* v7 migrated Users will automatically be set to No Role in order to protect information security; the business administrator will need to set each user to the most appropriate new roles after migrated is completed. +### Security groups + +- Security groups have been replaced by Roles. +- No v7 security group information or settings are migrated into v8. +- v7 migrated Users will automatically be set to No Role in order to protect information security; the business administrator will need to set each user to the most appropriate new roles after migrated is completed. For details see the [Authorization roles](ay-biz-admin-roles.md) guide for more information. ### Clients -* v7 Clients have been renamed to "Customers" + +- v7 Clients have been renamed to "Customers" + +### Parts + +- In v7 Parts had Part number and Part Name fields. Part Number was always intended as the _primary_ identifier for a part however in v8 we've changed Parts to use the more consistent Part Name as the _primary_ identifier. Users should use the Part Name field in v8 as the primary and sole identifier for a Part wherever possible. This field like all v8 name fields has no practical length limit and should be the main field used to identify parts both internally and externally. + +In order to accomodate this change, the V8 migration utility offers two ways to migrate the existing part fields as an optional choice before migration: + +1. Consolidate the v7 Part number and Part name fields into the single v8 Part Name field `{partnumber} {partname}` (default and recommended for most) +2. Import the v7 Part number field into the v8 Part name field and the v7 Part's 'Name' field into the v8 Part's 'Description' field ### Unit Models -* In v8 Unit Models no longer have the "Model Number" field. The "Name" field is now the primary identifier. Migrated v7 Unit Models will have their Model Number and Name fields combined into the single v8 Name field. + +- In v8 Unit Models no longer have the "Model Number" field. The "Name" field is now the primary identifier. Migrated v7 Unit Models will have their Model Number and Name fields combined into the single v8 Name field. ### Schedule markers @@ -43,7 +59,7 @@ V8 migrate will migrate the Schedule marker to the appropriate new object type a Schedule markers that are a Follow up type (tied to a particular object) are migrated to the new "Review" type object in AyaNova 8. Reviews do not have colors or Start or Stop dates so those properties are not directly migrated however the dates do influence some other properties: -The dates are migrated specially depending on if the v7 schedule marker Stop date has passed or not: If the v7 stop date has passed then the v8 Review record has it's "Review" date set to the v7 Start date and it's "Completed" date set to the v7 Stop date. If the v7 stop date has NOT passed then the v8 "Completed" date is left empty and the v8 "Review" date is set to the v7 Start date. +The dates are migrated specially depending on if the v7 schedule marker Stop date has passed or not: If the v7 stop date has passed then the v8 Review record has it's "Review" date set to the v7 Start date and it's "Completed" date set to the v7 Stop date. If the v7 stop date has NOT passed then the v8 "Completed" date is left empty and the v8 "Review" date is set to the v7 Start date. Reviews do not have a Completed checkbox to migrate as it has been superseded by a Completed date, however Reviews are tagged with a Completed Tag if they were completed in v7 in order to preserve that historical information. @@ -52,94 +68,102 @@ Reviews do not have a Completed checkbox to migrate as it has been superseded by Schedule markers that do not have a follow up link are migrated to v8 as "Reminder" type objects. All properties are migrated with the exception of the Completed checkbox: -Reminders do not have a Completed checkbox so that property is not migrated however they *are* tagged with a Completed Tag to preserve that information. +Reminders do not have a Completed checkbox so that property is not migrated however they _are_ tagged with a Completed Tag to preserve that information. ### Localized Text / Translation -* In V8 Localized text has been renamed to "Translation". -* V7 Localized text will only be migrated if it has been customized. Any customized Locales will be migrated to the best guess of source language into AyaNova 8. It tries to make a best guess as to which language each user was using but if it can't tell it defaults to the English based translation. You can adjust this after migrate for the users or they can set it themselves and you can delete any un-needed translations. +- In V8 Localized text has been renamed to "Translation". +- V7 Localized text will only be migrated if it has been customized. Any customized Locales will be migrated to the best guess of source language into AyaNova 8. It tries to make a best guess as to which language each user was using but if it can't tell it defaults to the English based translation. You can adjust this after migrate for the users or they can set it themselves and you can delete any un-needed translations. **IMPORTANT NOTE ABOUT MIGRATED TRANSLATIONS** V8Migrate will migrate customized translations from v7 and set the migrated Users to that same translation, however, we **strongly recommend** that you do not actually use the migrated translation once familiar with where everything you need appears in v8 but rather replicate any custom translation changes required in one of the stock V8 translations to avoid confusion as several objects have been renamed for clarity. -For example if your default translation is English and you have renamed an object or a phrase in v7, make a duplicate copy of the 'EN' default stock translation in V8 and edit the object or phrase in *that* translation and set it for your Users rather than continuing to use the migrated translation. +For example if your default translation is English and you have renamed an object or a phrase in v7, make a duplicate copy of the 'EN' default stock translation in V8 and edit the object or phrase in _that_ translation and set it for your Users rather than continuing to use the migrated translation. This will avoid issues with documentation and some areas of the UI being in conflict with other areas and ensure the new, clearer translations are displayed. - ### Wiki embedded files -* In V8 Wiki embedded files are now [Attachments](ay-start-form-attachments.md) and are separate from the Wiki page. -* In v7 only a limited set of objects could have Wiki pages and embedded files, now, all business objects support Attachments + +- In V8 Wiki embedded files are now [Attachments](ay-start-form-attachments.md) and are separate from the Wiki page. +- In v7 only a limited set of objects could have Wiki pages and embedded files, now, all business objects support Attachments ### Tags + [Tags](ay-start-form-tags.md) are a new feature for AyaNova 8 that replace and improve upon several different categorization features in v7. The following objects from v7 will be migrated as tags in v8: -* User certification -* User skill -* Client group -* Dispatch zone -* Part category -* Regions -* Scheduleable user group -* Unit service type -* Unit model category -* Vendor type -* Work order category -* Work order item type + +- User certification +- User skill +- Client group +- Dispatch zone +- Part category +- Regions +- Scheduleable user group +- Unit service type +- Unit model category +- Vendor type +- Work order category +- Work order item type Objects migrated to v8 will automatically be tagged with the corresponding tag that replaces the above feature. ### Wiki pages + [Wiki](ay-start-form-wiki.md) pages have change substantially in v8 based on user feedback. -* In v7 only a limited set of objects could have Wiki pages and embedded files, now, all business objects support Wiki pages -* The migrate process will by necessity need to discard some unsupported formatting elements from the v7 format wiki pages. -* Things that will be lost in the migration process are: - * Colors - * Font sizes - * Font faces (i.e. "Arial", "Courier" etc) - * Text alignment (left, center, right) - * internal AyaNova links to objects (for example linking directly to a Client record inside a wiki page) -* Things that will be kept: - * All entered text - * Lists - * Bolded text - * Underlined text - * Italicized text - * Images - * External URL links - * Wiki embedded files (will be migrated to new attachments feature) +- In v7 only a limited set of objects could have Wiki pages and embedded files, now, all business objects support Wiki pages +- The migrate process will by necessity need to discard some unsupported formatting elements from the v7 format wiki pages. +- Things that will be lost in the migration process are: + - Colors + - Font sizes + - Font faces (i.e. "Arial", "Courier" etc) + - Text alignment (left, center, right) + - internal AyaNova links to objects (for example linking directly to a Client record inside a wiki page) +- Things that will be kept: + - All entered text + - Lists + - Bolded text + - Underlined text + - Italicized text + - Images + - External URL links + - Wiki embedded files (will be migrated to new attachments feature) ### Users + Users are migrated directly with the following exceptions: -* Security group, now Role, see above -* Login credentials and password are not migrated and will need to be set -* Active status is set to false on all migrated users except the Adminstrator account -* Administrator account password is not migrated, instead the new v8 SuperUser account replaces it and defaults to login "superuser" and password "l3tm3in" -* User UI Color is not migrated as it is not used in AyaNova 8 + +- Security group, now Role, see above +- Login credentials and password are not migrated and will need to be set +- Active status is set to false on all migrated users except the Adminstrator account +- Administrator account password is not migrated, instead the new v8 SuperUser account replaces it and defaults to login "superuser" and password "l3tm3in" +- User UI Color is not migrated as it is not used in AyaNova 8 ### Service bank -The service bank feature has not been ported to AyaNova 8 as it does not appear to be widely used and would need extensive re-working. We could be wrong about this and if the Service Bank feature is important to you please let us know and how you would like to see it implemented as a feature. If there is demand for it we can work it back into AyaNova 8 as a feature based on your feedback. No data is migrated from the v7 Service Bank to v8. + +The service bank feature has not been ported to AyaNova 8 as it does not appear to be widely used and would need extensive re-working. We could be wrong about this and if the Service Bank feature is important to you please let us know and how you would like to see it implemented as a feature. If there is demand for it we can work it back into AyaNova 8 as a feature based on your feedback. No data is migrated from the v7 Service Bank to v8. ### Inventory -To ensure the inventory balances match, all parts are initially migrated to v8 with one billion in stock quantity. Then all inventory related objects are migrated such as Purchase Orders, Inventory adjustments and Service Work orders. Finally at the end of migration inventory transactions are made to the v8 inventory levels to bring the quantities on hand into balance with the v7 quantity on hand for each part. In AyaNova 8 inventory is consumed the moment a Part is saved on a work order which differs from v7 where inventory is consumed when a Part is set to Used in service on a work order. - +To ensure the inventory balances match, all parts are initially migrated to v8 with one billion in stock quantity. Then all inventory related objects are migrated such as Purchase Orders, Inventory adjustments and Service Work orders. Finally at the end of migration inventory transactions are made to the v8 inventory levels to bring the quantities on hand into balance with the v7 quantity on hand for each part. In AyaNova 8 inventory is consumed the moment a Part is saved on a work order which differs from v7 where inventory is consumed when a Part is set to Used in service on a work order. #### Work order item parts not "Used in service" -v8 work order item part affects inventory immediately when entered and saved but in v7 inventory is not affected until "Used in service" is checkmarked. For this reason, v7 work orders with work order item parts **not** checkmarked Used in service will have those quantities migrated to the "Suggested quantity" field instead of the Quantity field. This is to ensure that inventory remains in balance after migration. +v8 work order item part affects inventory immediately when entered and saved but in v7 inventory is not affected until "Used in service" is checkmarked. For this reason, v7 work orders with work order item parts **not** checkmarked Used in service will have those quantities migrated to the "Suggested quantity" field instead of the Quantity field. This is to ensure that inventory remains in balance after migration. In v8 Users will need to either use the "Realize suggested part quantities" command in the work order to automatically copy the suggested amounts to the Quantity field in all work order item part records for that work order or manually enter each one in the quantity field as appropriate. #### Inventory adjustments + Inventory and Serial numbers work differently in v8; Inventory adjustments can not be migrated with their original entry dates as the new v8 inventory "blockchain" system does not allow entries out of order so they are migrated as Inventory Transactions in v8 and the description of the transaction is named in this pattern: `v7Adjustment {v7 inventory adjustment ID number} {v7 adjustment "reason" field} {v7 adjustment "Date Adjusted"}` For example "v7Adjustment 45 Store stock opening inventory adjustment on 2005-11-27 10:50:53 AM". #### Serial numbers -Serial numbers also differ in v8 as they are now separated from inventory completely. All serials are migrated into the new v8 serial number system and can be viewed and verified on the Part list "Available serial numbers" column or from within a Part form by selecting the "Available serial numbers" menu item to open the edit form for editing Serial numbers for that part. + +Serial numbers also differ in v8 as they are now separated from inventory completely. All serials are migrated into the new v8 serial number system and can be viewed and verified on the Part list "Available serial numbers" column or from within a Part form by selecting the "Available serial numbers" menu item to open the edit form for editing Serial numbers for that part. ### Closed / Service Completed Work orders -V9 does not have a Service Completed or Closed checkbox. Instead those features are replaced by the new status system in v8. + +V9 does not have a Service Completed or Closed checkbox. Instead those features are replaced by the new status system in v8. During migration two locking type Work order status are created automatically "Closed (v7)" and "Service Completed (v7)". The closed status is a locked type to prevent editing and also a Completed type to signal that the work order is completed and no further edits are anticipated which replaces the Closed checkbox in v7. @@ -155,12 +179,14 @@ The original status in v7 is preserved in v8 as v8 status are now a collection r ## What you need to do after migration ### View the migration log + A copy of the migration log is automatically sent in AyaNova 8 in a Memo to the SuperUser account and is a mirror copy of the migration log displayed during migration. This log may contain important instructions and indications of any objects that could not be migrated or will need attention. Be sure to check this log carefully before proceeding and take any actions recommended. ### Check your data + Ensure that your data has migrated by comparing and confirming v7 to v8 objects in both AyaNova 7 and AyaNova 8. You should keep a copy of AyaNova 7 available for some time after migration for reference purposes in case any questions arise. @@ -172,34 +198,27 @@ Note: **Inactive users do not display in the v8 schedule** - in v8 the schedule All active users will need to have some settings made: -* Role - you will need to select one or more Authorization Roles to grant the User access to AyaNova. -* Login and Password - These are not migratable and need to be set for the user to log in, they can change their login and password themselves once they log in. -* Translation - The migration process will make a guess as to the most appropriate translation but you may need to adjust this setting -* Active - users are migrated as Inactive and must be set to Active - -Once Users are able to login they will need to subscribe to any [Notifications](home-notify-subscriptions.md) they require. The V8 Notification system is simplified from v7 and easier to set up but it can't be migrated as they are very different systems. +- Role - you will need to select one or more Authorization Roles to grant the User access to AyaNova. +- Login and Password - These are not migratable and need to be set for the user to log in, they can change their login and password themselves once they log in. +- Translation - The migration process will make a guess as to the most appropriate translation but you may need to adjust this setting +- Active - users are migrated as Inactive and must be set to Active +Once Users are able to login they will need to subscribe to any [Notifications](home-notify-subscriptions.md) they require. The V8 Notification system is simplified from v7 and easier to set up but it can't be migrated as they are very different systems. ### Customize Translations -If you had previously been using a customized Locale in v7 read the important note regarding this in the section above. We recommend not using the migrated translation but rather replicating any changes needed in a copy of a v8 stock language translation and setting User's to *that* translation rather than the one the v8Migrate has created. +If you had previously been using a customized Locale in v7 read the important note regarding this in the section above. We recommend not using the migrated translation but rather replicating any changes needed in a copy of a v8 stock language translation and setting User's to _that_ translation rather than the one the v8Migrate has created. ### Re-create report templates -If you have any customized report templates they will need to be re-created or re-customized in AyaNova 8. If your only customization was related to logos you may find they will just work in AyaNova 8 without any customization so be sure to set your logo if it hasn't been set already. - - - +If you have any customized report templates they will need to be re-created or re-customized in AyaNova 8. If your only customization was related to logos you may find they will just work in AyaNova 8 without any customization so be sure to set your logo if it hasn't been set already. ### RI / WBI Client access settings -The V8Migrate utility is not able to access settings in the optional add-on's RI and WBI and so is not able to automatically migrate their settings to AyaNova 8. Instead it will automatically choose safe defaults for those settings and lock out Customer access. +The V8Migrate utility is not able to access settings in the optional add-on's RI and WBI and so is not able to automatically migrate their settings to AyaNova 8. Instead it will automatically choose safe defaults for those settings and lock out Customer access. In v7 you would make those settings within the interface provided by those optional add-on's, in AyaNova 8 all those settings can be found under Administration -> Global Settings in the "Customer Access" section and need to be set manually. ### Enable Backup -AyaNova 8 has a built in [backup system](ops-form-backup.md) that will back up to the local drive automatically. It is disabled by default and must be enabled to work. - - - +AyaNova 8 has a built in [backup system](ops-form-backup.md) that will back up to the local drive automatically. It is disabled by default and must be enabled to work. diff --git a/docs/8.0/ayanova/docs/ay-start-changes-from-v7.md b/docs/8.0/ayanova/docs/ay-start-changes-from-v7.md index 07e51e7e..1239264f 100644 --- a/docs/8.0/ayanova/docs/ay-start-changes-from-v7.md +++ b/docs/8.0/ayanova/docs/ay-start-changes-from-v7.md @@ -8,31 +8,35 @@ Our goals in this were to make AyaNova easier to use, modernize aging code, take This page documents the changes most relevant to everyday users of AyaNova however there were also many other technical changes of interest to system administrators and installers which are documented in our [Technical changes from v7](ops-technical-changes-from-v7.md) page of this documentation. ## User interface changes from v7 -AyaNova 7 had several different user interfaces: the main interface was a Windows desktop application and as optional add-on products we had several different web browser interfaces for varying levels of devices. Each web interface had a limited sub-set of the full features in the Windows application and required separate installation and configuration procedures. -AyaNova 8 has one "progressive" web application based interface only that adapts itself automatically to the device format. Any device on any platform with a modern web browser from a smart phone to a full sized desktop computer can use all features of AyaNova equally. This means you can use AyaNova on an iPhone, Android, Windows desktop, Mac, Linux etc. +AyaNova 7 had several different user interfaces: the main interface was a Windows desktop application and as optional add-on products we had several different web browser interfaces for varying levels of devices. Each web interface had a limited sub-set of the full features in the Windows application and required separate installation and configuration procedures. + +AyaNova 8 has one "progressive" web application based interface only that adapts itself automatically to the device format. Any device on any platform with a modern web browser from a smart phone to a full sized desktop computer can use all features of AyaNova equally. This means you can use AyaNova on an iPhone, Android, Windows desktop, Mac, Linux etc. Having one web application interface means there is nothing to install or update on all user devices and ensures that we will always have all features available to all users. -In order to support the front end "client" devices using AyaNova, there is now a back end self contained [AyaNova server](ops-intro.md) that handles all requests and can be hosted on Linux, Windows, Mac either onsite or with a web hosting provider. - +In order to support the front end "client" devices using AyaNova, there is now a back end self contained [AyaNova server](ops-intro.md) that handles all requests and can be hosted on Linux, Windows, Mac either onsite or with a web hosting provider. ## Features dropped from v7 to v8 ### Service bank -The service bank feature has not been ported to AyaNova 8 as it does not appear to be widely used and would need extensive re-working. We could be wrong about this and if the Service Bank feature is important to you please let us know and how you would like to see it implemented as a feature. If there is demand for it we can work it back into AyaNova 8 as a feature based on your feedback. + +The service bank feature has not been ported to AyaNova 8 as it does not appear to be widely used and would need extensive re-working. We could be wrong about this and if the Service Bank feature is important to you please let us know and how you would like to see it implemented as a feature. If there is demand for it we can work it back into AyaNova 8 as a feature based on your feedback. ## Existing feature changes + One of our main goals with AyaNova 8 was to make AyaNova easier to use and add many new features and improvements based on feedback from our customers. In order to support this some major areas of AyaNova have been changed: ### Clients renamed + Clients have been renamed to Customers ### Client User's and Contacts + Customers in AyaNova 8 now have unlimited Contacts as a seperate collection of User type objects so you optionally allow them to login to use the Customer interface and subscribe to Customer specific notifications etc. -Migrating from AyaNova 7 will result in a Customer Contact type User being created for each v7 Customer or HeadOffice type User account and in addition a Contact will be created in v8 if the v7 Client has a name set in the "Contact" text field with the Client phone and email numbers being re-used for the contact created this way. The Contact notes text field in v7 that is intended for listing extra contacts will be migrated to the v8 general Customer Notes field as there is no way to safely migrate that freehand text field as additional Contact type users in v8. +Migrating from AyaNova 7 will result in a Customer Contact type User being created for each v7 Customer or HeadOffice type User account and in addition a Contact will be created in v8 if the v7 Client has a name set in the "Contact" text field with the Client phone and email numbers being re-used for the contact created this way. The Contact notes text field in v7 that is intended for listing extra contacts will be migrated to the v8 general Customer Notes field as there is no way to safely migrate that freehand text field as additional Contact type users in v8. As with all User / Contact accounts created in v8 no login is possible by those users until the administrator allows it and assigns a password so there is no security risk of Contact users logging in that previously could not. @@ -42,13 +46,13 @@ The AyaNova 8 inventory system is completely new and works differently than the It is both more flexible as it allows for easily changing values after the fact but more strict in that it has a far more strict control over inventory values than AyaNova 7 allowed. -V8 inventory is "live" and affected instantly when a PO or Work Order are saved with any *changes* of the Received or consumed quantity (respectively) for a part. This means that when you receive 10 parts on a PO they will be available for use immediately on work orders etc. If the PO is then edited to change the 10 to a 5, the inventory is immediately updated to remove the 5 that are now not received. +V8 inventory is "live" and affected instantly when a PO or Work Order are saved with any _changes_ of the Received or consumed quantity (respectively) for a part. This means that when you receive 10 parts on a PO they will be available for use immediately on work orders etc. If the PO is then edited to change the 10 to a 5, the inventory is immediately updated to remove the 5 that are now not received. -There is no longer a "Used in service" checkbox in Work orders, they consume inventory immediately upon entry and save. +There is no longer a "Used in service" checkbox in Work orders, they consume inventory immediately upon entry and save. -If a work order consumes a quantity of 5 for a part and is saved that amount is *immediately* removed from inventory. If the work order is subsequently edited to change the 5 to a 3 then a quantity of 2 is immediately returned to inventory upon saving the changed work order. +If a work order consumes a quantity of 5 for a part and is saved that amount is _immediately_ removed from inventory. If the work order is subsequently edited to change the 5 to a 3 then a quantity of 2 is immediately returned to inventory upon saving the changed work order. -All inventory changes are tracked in a blockchain inside your AyaNova database and this block chain can be viewed in the Inventory transactions data table. Inventory transactions are required for any change of inventory and the inventory transaction blockchain is the only repository for inventory quantities in AyaNova. +All inventory changes are tracked in a blockchain inside your AyaNova database and this block chain can be viewed in the Inventory transactions data table. Inventory transactions are required for any change of inventory and the inventory transaction blockchain is the only repository for inventory quantities in AyaNova. AyaNova 8 does not permit negative on hand inventory values to exist, every part consumed must have a quantity of on-hand inventory to back it up or it's not allowed to be saved. @@ -57,85 +61,106 @@ AyaNova 8 does not permit negative on hand inventory values to exist, every part If there is a need to enter parts on a work order as a placeholder so as not to affect inventory for planning purposes the work order item part's "Suggested Quantity" field can be utilized for this purpose. ### Purchase orders / receipts -Purchase orders and receipts have been combined in v8 and are no longer separate objects. -The various rules around Purchase order editing have been eliminated. In v8 purchase order editing ability is no longer controlled by their Status and a PO can be edited anywhere any time. Behind the scenes AyaNova will make the necessary adjustments to inventory to support any edit changes made to purchase orders if they have already affected inventory or work order item part requests. This allows for easy edits when things change post order, for example if a supplier has changed a price or an item has been replaced by another one after being ordered. +Purchase orders and receipts have been combined in v8 and are no longer separate objects. -Status is still available and can be set to any value at any time as users need for displaying in lists and controlling the process. AyaNova itself will not set the PO status automatically at any time. +The various rules around Purchase order editing have been eliminated. In v8 purchase order editing ability is no longer controlled by their Status and a PO can be edited anywhere any time. Behind the scenes AyaNova will make the necessary adjustments to inventory to support any edit changes made to purchase orders if they have already affected inventory or work order item part requests. This allows for easy edits when things change post order, for example if a supplier has changed a price or an item has been replaced by another one after being ordered. + +Status is still available and can be set to any value at any time as users need for displaying in lists and controlling the process. AyaNova itself will not set the PO status automatically at any time. + +### Part Number / primary identifier fields + +In v7 Parts had Part number and Part Name fields. Part Number was always intended as the _primary_ identifier for a part however in v8 we've changed Parts to use the more consistent Part Name as the _primary_ identifier. Users should use the Part Name field in v8 as the primary and sole identifier for a Part wherever possible. This field like all v8 name fields has no practical length limit and should be the main field used to identify parts both internally and externally. + +In order to accomodate this change, the V8 migration utility offers two ways to migrate the existing part fields as an optional choice before migration: + +1. Consolidate the v7 Part number and Part name fields into the single v8 Part Name field `{partnumber} {partname}` (default and recommended for most) +2. Import the v7 Part number field into the v8 Part name field and the v7 Part's 'Name' field into the v8 Part's 'Description' field ### Part serial numbers + Serial numbers have been separated from inventory for v8. -They now reside in a collection of serials for each Part and you can select from the list or enter alternatives or none at all at any time. This supports the many cases where users requested the ability to be more free-form with their serial numbers to allow for mistakes or unique needs but still record them in the system when they are consumed on work orders for warranty purposes etc. +They now reside in a collection of serials for each Part and you can select from the list or enter alternatives or none at all at any time. This supports the many cases where users requested the ability to be more free-form with their serial numbers to allow for mistakes or unique needs but still record them in the system when they are consumed on work orders for warranty purposes etc. Migrating from v7 will place any existing serials into the serial number collection automatically. ### Travel and Service Rates -Similar to how Tax Codes worked in AyaNova 7, rates will now have their price affecting fields (Charge, Cost) frozen once that Rate has been assigned to any object to preserve data integrity. + +Similar to how Tax Codes worked in AyaNova 7, rates will now have their price affecting fields (Charge, Cost) frozen once that Rate has been assigned to any object to preserve data integrity. ### Service Work orders #### Flexibility changes -User interface: The user interface for the work order has been designed to reduce the clutter on screen as much as possible while still using the fewest possible clicks to navigate around from section to section. In addition we've ensured that a work order can be fully completed using a device as narrow as a 360 pixel wide smart phone if necessary. +User interface: The user interface for the work order has been designed to reduce the clutter on screen as much as possible while still using the fewest possible clicks to navigate around from section to section. In addition we've ensured that a work order can be fully completed using a device as narrow as a 360 pixel wide smart phone if necessary. -Decoupling of sections to reduce conflict: In v7 an entire work order was updated at once in the database meaning only one person at a time could edit **any part** of that work order and save without conflict. In v8 we have broken out the work order save process into each individual section so the work order Header is one section, a work order Item is another, a work order item part it's own section etc. This means if two (or more) people are working separately in the same work order, as long as what they are not working in the same section there is no conflict. For example two people could be working with two different work order items at the same time and save independantly of each other or two or more people could be entering parts separately in the same work order item without conflict. Note that there are still circumstances where a conflict can occur and we still recommend people take steps to avoid working in the same areas at the same time, but conflicts will be greatly reduced. V8 still uses the first save wins method of conflict resolution so that no one can save and lose their changes without knowing it. +Decoupling of sections to reduce conflict: In v7 an entire work order was updated at once in the database meaning only one person at a time could edit **any part** of that work order and save without conflict. In v8 we have broken out the work order save process into each individual section so the work order Header is one section, a work order Item is another, a work order item part it's own section etc. This means if two (or more) people are working separately in the same work order, as long as what they are not working in the same section there is no conflict. For example two people could be working with two different work order items at the same time and save independantly of each other or two or more people could be entering parts separately in the same work order item without conflict. Note that there are still circumstances where a conflict can occur and we still recommend people take steps to avoid working in the same areas at the same time, but conflicts will be greatly reduced. V8 still uses the first save wins method of conflict resolution so that no one can save and lose their changes without knowing it. -Decoupling of Client: In v7 a Client was tightly connected to a work order and needed to be selected prior to creation of a work order and could not be changed. We've made many behind the scenes structural changes to remove this limitation. In v8 the Customer can be changed at any time for the work order as a whole. +Decoupling of Client: In v7 a Client was tightly connected to a work order and needed to be selected prior to creation of a work order and could not be changed. We've made many behind the scenes structural changes to remove this limitation. In v8 the Customer can be changed at any time for the work order as a whole. -Decoupling of Contract: In v7 a contract was tighly connected to a particular Client and a work order in turn was tighly coupled to the Contract selected. In v8 the work order structure has been changed to allow a Contract to be changed at any time for a work order. Now, a Contract will be automatically applied to a work order if a Customer, Head Office or Unit is selected for the work order and it has no current contract selected. In addition the Contract selected is exposed as a user selectable item so any contract can be applied at any time to any work order or removed if not appropriate. +Decoupling of Contract: In v7 a contract was tighly connected to a particular Client and a work order in turn was tighly coupled to the Contract selected. In v8 the work order structure has been changed to allow a Contract to be changed at any time for a work order. Now, a Contract will be automatically applied to a work order if a Customer, Head Office or Unit is selected for the work order and it has no current contract selected. In addition the Contract selected is exposed as a user selectable item so any contract can be applied at any time to any work order or removed if not appropriate. -Work order status improvements: In v7 the work order status was a single value stored with the work order selectable by anyone and served only as a flag. In v8 the Work order Status has been greatly expanded into a process control feature and is now a collection that is appended to each time a status changes. In the work order a single status still shows but can be clicked on to see the entire history of all status changes. In addition Work order status now has options to restrict which security Roles can select a particular status or change out of a currently selected particular status. As well, a status can be flagged as being a "Locked" status which will prevent edits to the work order and can be flagged as a "Completed" type status for triggering notifications etc. The combination of these features makes it easir to control the service process as a work order flows through the service process. For example it is now easy to make a status for the purpose of a Service Manager or Accounting person to lock a work order at a certain point to inspect it before unlocking it to move on to the next step in the process or to trigger a particular notification at a particular point. (Note that Work order **Item** status is still a feature but is a separate collection now from the main work order status and remains the same as the v7 work order **item** status as a single selectable flag.) +Work order status improvements: In v7 the work order status was a single value stored with the work order selectable by anyone and served only as a flag. In v8 the Work order Status has been greatly expanded into a process control feature and is now a collection that is appended to each time a status changes. In the work order a single status still shows but can be clicked on to see the entire history of all status changes. In addition Work order status now has options to restrict which security Roles can select a particular status or change out of a currently selected particular status. As well, a status can be flagged as being a "Locked" status which will prevent edits to the work order and can be flagged as a "Completed" type status for triggering notifications etc. The combination of these features makes it easir to control the service process as a work order flows through the service process. For example it is now easy to make a status for the purpose of a Service Manager or Accounting person to lock a work order at a certain point to inspect it before unlocking it to move on to the next step in the process or to trigger a particular notification at a particular point. (Note that Work order **Item** status is still a feature but is a separate collection now from the main work order status and remains the same as the v7 work order **item** status as a single selectable flag.) -Address is now stored with the work order itself rather than being linked to the Client / Customer or head office. The Customer address will be used unless Bill head office is in effect in which case the Head office address will be the default. In addition there is now a method to quickly select from any relevant address in the work order itself if the User wants to change the entire Workorder address to (for example) one of the Unit's addresses on that work order or type in an alternative value. This preserves history and makes the Work order more flexible for addressing. +Address is now stored with the work order itself rather than being linked to the Client / Customer or head office. The Customer address will be used unless Bill head office is in effect in which case the Head office address will be the default. In addition there is now a method to quickly select from any relevant address in the work order itself if the User wants to change the entire Workorder address to (for example) one of the Unit's addresses on that work order or type in an alternative value. This preserves history and makes the Work order more flexible for addressing. #### Work order Templates and Duplicate / copy to -Work orders can now be duplicated like all other objects in AyaNova 8. In addition a Work order can be copied to a new Quote or new Preventive Maintenance object. In fact there is the ability now in v8 to convert any quote, work order or preventive maintenance into any other one of those objects, so, for example a Preventive Maintenance order can be turned into a Quote via a Work order or a Work order can be converted into a PM or Quote directly. +Work orders can now be duplicated like all other objects in AyaNova 8. In addition a Work order can be copied to a new Quote or new Preventive Maintenance object. In fact there is the ability now in v8 to convert any quote, work order or preventive maintenance into any other one of those objects, so, for example a Preventive Maintenance order can be turned into a Quote via a Work order or a Work order can be converted into a PM or Quote directly. For this reason the v7 templates feature is no longer required and has not been ported to v8. #### Work order status (Closed / service completed replacement) The work order status feature has been greatly expanded into a set of powerful features that can support process control with: -* tracking of every change of status (who and when visible right inside the work order) -* restrictions by security role of who can set or unset any status (this allows for controlling the entire service process from creation to final billing or follow up) -* locking status which when selected freezes a work order (replaces the v7 "Service completed" checkbox) -* completed type status to indicate a work order is completed which can be used with notification etc (replaces the v7 "Closed" checkbox) + +- tracking of every change of status (who and when visible right inside the work order) +- restrictions by security role of who can set or unset any status (this allows for controlling the entire service process from creation to final billing or follow up) +- locking status which when selected freezes a work order (replaces the v7 "Service completed" checkbox) +- completed type status to indicate a work order is completed which can be used with notification etc (replaces the v7 "Closed" checkbox) These new features are optional and not necessary to use if just a simple status indicator is desired and it can be used in much the same way as in v7 if there isn't a need for the extra features. #### Customization -Now nearly all fields and sections on the work order form can be removed or made mandatory for entry. We've made design changes behind the scenes to reduce as much as possible the number of manadatory fields that must be filled in so that users can remove more things from the interface through customization if they don't require them. + +Now nearly all fields and sections on the work order form can be removed or made mandatory for entry. We've made design changes behind the scenes to reduce as much as possible the number of manadatory fields that must be filled in so that users can remove more things from the interface through customization if they don't require them. #### Custom fields / attachments / wiki -In v7 custom fields, attachments and wiki were supported only in the Work order Item part of the work order. We have now added those to the main Work order itself and the Work order item Unit record in addition to the work order item. So there are now three areas in a work order that support these features. Migrated data from v7 will go to the Work order item record the same as v7. -#### Inventory +In v7 custom fields, attachments and wiki were supported only in the Work order Item part of the work order. We have now added those to the main Work order itself and the Work order item Unit record in addition to the work order item. So there are now three areas in a work order that support these features. Migrated data from v7 will go to the Work order item record the same as v7. + +#### Inventory ##### Serial numbers -When a work order has a part added / removed or changed, inventory and serial numbers are updated immediately upon save. There is no longer a "Used in service" checkbox to affect inventory; inventory is now considered immediately affected upon save of a work order item part record. This resolves a number of issues that could arise in the past. + +When a work order has a part added / removed or changed, inventory and serial numbers are updated immediately upon save. There is no longer a "Used in service" checkbox to affect inventory; inventory is now considered immediately affected upon save of a work order item part record. This resolves a number of issues that could arise in the past. Serial numbers can be edited directly from the Part record and are not tied to inventory in v8. ##### Part inventory adjustments -In v7 a part inventory adjustment was a separate type of object from the actual inventory itself. In v8 inventory is adjusted by making an inventory transaction entry directly into the inventory "blockchain". In v8 serial numbers are updated via the Part object, *not* the inventory adjustment / transaction as in v7. + +In v7 a part inventory adjustment was a separate type of object from the actual inventory itself. In v8 inventory is adjusted by making an inventory transaction entry directly into the inventory "blockchain". In v8 serial numbers are updated via the Part object, _not_ the inventory adjustment / transaction as in v7. #### Work order Items -The work order items now have a "Sequence" field and can be re-ordered as desired so that they are listed in the UI and print on work orders in a specific order. If no order is chosen it defaults to the v7 system of newer items appended to the bottom of the list. + +The work order items now have a "Sequence" field and can be re-ordered as desired so that they are listed in the UI and print on work orders in a specific order. If no order is chosen it defaults to the v7 system of newer items appended to the bottom of the list. #### Work order item Units -Units are now a *collection* under work order item rather than a single item. This facilitates scenarios where a large number of units need to be serviced identically at the same time (inspection, maintenance etc). Users can choose to select multiple units or a single unit as appropriate. + +Units are now a _collection_ under work order item rather than a single item. This facilitates scenarios where a large number of units need to be serviced identically at the same time (inspection, maintenance etc). Users can choose to select multiple units or a single unit as appropriate. #### Work order item Outside Service -Outside service is now a *collection* under work order item rather than a single item to mirror the change for Units. Now, in the Outside service section there is a Unit selection control for setting to which Unit the Outside service applies. + +Outside service is now a _collection_ under work order item rather than a single item to mirror the change for Units. Now, in the Outside service section there is a Unit selection control for setting to which Unit the Outside service applies. #### Work order item Tasks -Tasks have been changed to a collection stored *with* the work order rather than linked to a separate collection of tasks in a task group. Now, a user can simply type in a list of tasks or still select from a task group which will be used to fill in the work order rather than link to it so it can be edited as needed. In addition tasks are now re-orderable. + +Tasks have been changed to a collection stored _with_ the work order rather than linked to a separate collection of tasks in a task group. Now, a user can simply type in a list of tasks or still select from a task group which will be used to fill in the work order rather than link to it so it can be edited as needed. In addition tasks are now re-orderable. #### Pricing -Pricing and costs are now clearly displayed on the work order in a consistent manner between all billable items that have prices. List price and final price are both shown so that it's clear that a contract or manual override have affected the final price. Most non management roles will not see costs or prices by default. Most prices (except for Parts which tend to have volatile pricing) are not stored with the work order but instead calculated on the fly when a work order is opened / printed. This is part of the decoupling process that allows for easily changing the Contract or Customer on an existing Work order. This means that similar to v7 some items like taxes and rates cannot be changed once they have been used on a work order however Parts are an exception as the pricing is considered more volatile so part prices are "snapshotted" on being added to a work order. +Pricing and costs are now clearly displayed on the work order in a consistent manner between all billable items that have prices. List price and final price are both shown so that it's clear that a contract or manual override have affected the final price. Most non management roles will not see costs or prices by default. Most prices (except for Parts which tend to have volatile pricing) are not stored with the work order but instead calculated on the fly when a work order is opened / printed. This is part of the decoupling process that allows for easily changing the Contract or Customer on an existing Work order. This means that similar to v7 some items like taxes and rates cannot be changed once they have been used on a work order however Parts are an exception as the pricing is considered more volatile so part prices are "snapshotted" on being added to a work order. #### Customer and Unit data list "Last completed... " columns @@ -144,101 +169,106 @@ In v8 the Customer and Unit data tables now have "Last work order" and "Last ser #### Unit Models -In v8 Unit Models no longer have the "Model Number" field. The "Name" field is now the primary identifier. Migrated v7 Unit Models will have their Model Number and Name fields combined into the single v8 Name field. +In v8 Unit Models no longer have the "Model Number" field. The "Name" field is now the primary identifier. Migrated v7 Unit Models will have their Model Number and Name fields combined into the single v8 Name field. ### Quotes -Quotes now provide all the same sections as a service Work order with the sole exception of Part Requests which are not applicable to Quotes. Menu options are available to directly duplicate a quote to a PM or Work order (and optionally WIKI and File Attachments as well). Quotes can also be duplicated to a new Quote (as can all main objects in v8) - +Quotes now provide all the same sections as a service Work order with the sole exception of Part Requests which are not applicable to Quotes. Menu options are available to directly duplicate a quote to a PM or Work order (and optionally WIKI and File Attachments as well). Quotes can also be duplicated to a new Quote (as can all main objects in v8) ### Preventive Maintenance ("PM") -PMs now provide all the same sections as a service Work order with the sole exception of Part Requests which are not applicable to PMs. Menu options are available to directly duplicate a PM to a Quote or Work order (and optionally WIKI and File Attachments as well). PMs can also be duplicated to a new PM. The "Desired day of week" to generate the Service Work Order on has been replaced by an "Exclude days of the week" control so instead of choosing a particular day to generate to now you would choose days *not* to generate on and the system will find the closest available day that matches that choice. +PMs now provide all the same sections as a service Work order with the sole exception of Part Requests which are not applicable to PMs. Menu options are available to directly duplicate a PM to a Quote or Work order (and optionally WIKI and File Attachments as well). PMs can also be duplicated to a new PM. The "Desired day of week" to generate the Service Work Order on has been replaced by an "Exclude days of the week" control so instead of choosing a particular day to generate to now you would choose days _not_ to generate on and the system will find the closest available day that matches that choice. ### Contracts -Service contracts have been expanded with new features and more flexibility when applying (or not) to work orders. You can now select a discount off list price or a markup on cost for special contract pricing. In addition to the v7 general discount in a contract you can now select specific tags for parts or rates that, if those items have the tags selected, will be discounted / marked up the value selected. This gives a lot of flexibility in cases where a certain class or type of part for example should be included in discounts but not others. There are now also features for maximum response time that tie into notification and close by date features of work order automatically for contracts that include a response time commitment. + +Service contracts have been expanded with new features and more flexibility when applying (or not) to work orders. You can now select a discount off list price or a markup on cost for special contract pricing. In addition to the v7 general discount in a contract you can now select specific tags for parts or rates that, if those items have the tags selected, will be discounted / marked up the value selected. This gives a lot of flexibility in cases where a certain class or type of part for example should be included in discounts but not others. There are now also features for maximum response time that tie into notification and close by date features of work order automatically for contracts that include a response time commitment. ### Localized text is now Translation + Localized text has been renamed to Translation - ### Security groups -> Authorization Roles + Security groups have been replaced by a **role** based authorization system. In v7 you would select a security group for a user to restrict their access to objects in AyaNova. -In v8 a user is now assigned one or more Authorization Roles which controls their access. Roles are pre-defined and pertain to the type of user for example there are roles for Inventory, Accounting, Business administration, Technician, Subcontractor etc. Whichever roles you assign to a user give them access to those areas of AyaNova. +In v8 a user is now assigned one or more Authorization Roles which controls their access. Roles are pre-defined and pertain to the type of user for example there are roles for Inventory, Accounting, Business administration, Technician, Subcontractor etc. Whichever roles you assign to a user give them access to those areas of AyaNova. -Migrating Users from AyaNova 7 will automatically set migrated Users to have no role in order to protect information security; the business administrator will need to set each user to the most appropriate new roles after migration is completed. For more about migrating from a business administration standpoint see the [business administration migration guide](ay-biz-admin-import-v7.md) and for more about the techncial aspects of the migration process see the [Migration technical guide](ops-import-v7.md). +Migrating Users from AyaNova 7 will automatically set migrated Users to have no role in order to protect information security; the business administrator will need to set each user to the most appropriate new roles after migration is completed. For more about migrating from a business administration standpoint see the [business administration migration guide](ay-biz-admin-import-v7.md) and for more about the techncial aspects of the migration process see the [Migration technical guide](ops-import-v7.md). For details see the [Authorization roles](ay-biz-admin-roles.md) guide for more information. ### Schedule markers -In AyaNova 7 there were two types of schedule markers: a regular one and a follow up type. The follow up type was tied to a particular object and provided a button to open that object. The regular type did not have a link to an object. Both had a "Completed" check box. +In AyaNova 7 there were two types of schedule markers: a regular one and a follow up type. The follow up type was tied to a particular object and provided a button to open that object. The regular type did not have a link to an object. Both had a "Completed" check box. In AyaNova 8 the Schedule marker has been replaced by two separate objects the "Reminder" and the "Review". #### Reminder -A Reminder is similar to a regular v7 Schedule marker and can not be tied to a particular object. It's purpose is to provide a place where a Note can be tied to a date and display in the Schedule form. Basically the original core purpose of a Schedule marker. +A Reminder is similar to a regular v7 Schedule marker and can not be tied to a particular object. It's purpose is to provide a place where a Note can be tied to a date and display in the Schedule form. Basically the original core purpose of a Schedule marker. #### Review -A Review is tied to a particular object and has a Review Date and a Completed date and also displays in the graphical Schedule. It's purpose is to replace the v7 Follow Up feature and is intended for exactly that purpose: to follow up or review the status of a particular object in AyaNova be it a Customer or Unit or some other main object type. +A Review is tied to a particular object and has a Review Date and a Completed date and also displays in the graphical Schedule. It's purpose is to replace the v7 Follow Up feature and is intended for exactly that purpose: to follow up or review the status of a particular object in AyaNova be it a Customer or Unit or some other main object type. ### Record history -In AyaNova 7 there was a Record history feature which would show who created an object and when it was created and who last edited an object and when. No intermediate changes were tracked, in other words you couldn't see any history of changes, only the most recent. -In v8 we have expanded this feature into a full log and renamed it the [Event log](ay-start-event-log.md). Now, (for all major objects) AyaNova will keep a log of every time an object was created, retrieved, updated and deleted and which user did it a log format so you can see a history of who made changes and when. +In AyaNova 7 there was a Record history feature which would show who created an object and when it was created and who last edited an object and when. No intermediate changes were tracked, in other words you couldn't see any history of changes, only the most recent. +In v8 we have expanded this feature into a full log and renamed it the [Event log](ay-start-event-log.md). Now, (for all major objects) AyaNova will keep a log of every time an object was created, retrieved, updated and deleted and which user did it a log format so you can see a history of who made changes and when. ### Tags + [Tags](ay-start-form-tags.md) are a new feature for AyaNova 8 that replace and improve upon several different categorization features in v7. The following objects from v7 have been replaced by tags in v8: -* User certification -* User skill -* Client group -* Dispatch zone -* Part category -* Regions -* Scheduleable user group -* Unit service type -* Unit model category -* Work order category -* Work order item type + +- User certification +- User skill +- Client group +- Dispatch zone +- Part category +- Regions +- Scheduleable user group +- Unit service type +- Unit model category +- Work order category +- Work order item type [Migrating from v7](ops-import-v7.md) will automatically create tags for these objects and will then tag the corresponding objects that were set to these category items with the new tags created. In addition, all objects migrated from v7 will be tagged with "v8-migrate". ### Reports -AyaNova 8 has a built in report template editor to create and customize existing reports, however, the reporting system is completely new and not compatible with v7 style report templates which relied on a 3rd party commercial reporting component. Reporting is now HTML based and uses Javascript as the report language and HTML / Mustache template system as the report design template. + +AyaNova 8 has a built in report template editor to create and customize existing reports, however, the reporting system is completely new and not compatible with v7 style report templates which relied on a 3rd party commercial reporting component. Reporting is now HTML based and uses Javascript as the report language and HTML / Mustache template system as the report design template. Reports that were customized in v7 and do not have a replacement provided with the stock v8 report templates will need to be re-created in v8. ### Wiki + The [Wiki](ay-start-form-wiki.md) feature has been changed substantially based on user feedback. - ##### Editor -Previously there was a hybrid Wiki system, remote users (WBI) would interact with an HTML editor and HTML representation of the wiki document and windows AyaNova users would interact with a "rich text" editor similar to Wordpad. This required behind the scenes translations between Rich text format and HTML format and the results were not always compatible. In addition, it would have been very difficult to edit Wiki pages on small screen devices. -Now, a Wiki document is edited using [Markdown](https://en.wikipedia.org/wiki/Markdown) a simple plain text formatting language. This allows us to provide an efficient and easy to use editor for Wiki pages that will work on any AyaNova supported device. +Previously there was a hybrid Wiki system, remote users (WBI) would interact with an HTML editor and HTML representation of the wiki document and windows AyaNova users would interact with a "rich text" editor similar to Wordpad. This required behind the scenes translations between Rich text format and HTML format and the results were not always compatible. In addition, it would have been very difficult to edit Wiki pages on small screen devices. + +Now, a Wiki document is edited using [Markdown](https://en.wikipedia.org/wiki/Markdown) a simple plain text formatting language. This allows us to provide an efficient and easy to use editor for Wiki pages that will work on any AyaNova supported device. + +##### Embedded files -##### Embedded files Previously users were able to embed files into a Wiki document which was in turn stored directly in the AyaNova database which could cause issues and lead to longer backup and restore times. Now, we have expanded this feature into it's own separate [Attachments](ay-start-form-attachments.md) feature ### Documents / Wiki Embedded files -> Attachments + All attached and embedded file features have been consolidated in v8 into a new [Attachments](ay-start-form-attachments.md) feature. -* Attachment storage - * in v7 embedded wiki files were stored *inside* the database, now Attachments are stored in a special folder in the file system to avoid bloating the database. - * In v7 Documents were simply links to existing files visible to the workstations / server; file attachments are stored in a special folder managed by the AyaNova server making it easier to backup and restore and ensure all data is consolidated in one folder. -* Rename - can now rename an attached file -* Notes - there is now a notes field - +- Attachment storage + - in v7 embedded wiki files were stored _inside_ the database, now Attachments are stored in a special folder in the file system to avoid bloating the database. + - In v7 Documents were simply links to existing files visible to the workstations / server; file attachments are stored in a special folder managed by the AyaNova server making it easier to backup and restore and ensure all data is consolidated in one folder. +- Rename - can now rename an attached file +- Notes - there is now a notes field ### Display formats -In v7 there were Global settings for Part display format, Unit display format etc. In v8 this feature has been replaced by [Select list](ay-start-form-select-list.md) templates which allow customizing nearly *all* select lists to choose how best to display them for selection in v8. - +In v7 there were Global settings for Part display format, Unit display format etc. In v8 this feature has been replaced by [Select list](ay-start-form-select-list.md) templates which allow customizing nearly _all_ select lists to choose how best to display them for selection in v8. diff --git a/server/AyaNova/DataList/PMItemPartDataList.cs b/server/AyaNova/DataList/PMItemPartDataList.cs index 057673a0..7d62574a 100644 --- a/server/AyaNova/DataList/PMItemPartDataList.cs +++ b/server/AyaNova/DataList/PMItemPartDataList.cs @@ -62,7 +62,7 @@ namespace AyaNova.DataList UiFieldDataType = (int)UiFieldDataType.Text, AType = (int)AyaType.PMItemPart, SqlIdColumnName = "apmitempart.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsRowId = true }); diff --git a/server/AyaNova/DataList/PartDataList.cs b/server/AyaNova/DataList/PartDataList.cs index 031c0245..4cb7fd53 100644 --- a/server/AyaNova/DataList/PartDataList.cs +++ b/server/AyaNova/DataList/PartDataList.cs @@ -10,27 +10,27 @@ namespace AyaNova.DataList SQLFrom = "from viewpartsdatalist "; var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; - DefaultColumns = new List() { "PartPartNumber", "PartName", "PartManufacturerID", "PartWholesalerID", "Tags" }; - DefaultSortBy = new Dictionary() { { "PartPartNumber", "+" } }; + DefaultColumns = new List() { "PartName", "PartManufacturerID", "PartWholesalerID", "Tags" }; + DefaultSortBy = new Dictionary() { { "PartName", "+" } }; FieldDefinitions = new List(); - FieldDefinitions.Add(new DataListFieldDefinition - { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", - AType = (int)AyaType.Part, - UiFieldDataType = (int)UiFieldDataType.Text, - SqlIdColumnName = "id", - SqlValueColumnName = "partnumber", - IsRowId = true - }); - FieldDefinitions.Add(new DataListFieldDefinition { TKey = "PartName", FieldKey = "PartName", + AType = (int)AyaType.Part, UiFieldDataType = (int)UiFieldDataType.Text, - SqlValueColumnName = "name" + SqlIdColumnName = "id", + SqlValueColumnName = "name", + IsRowId = true + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PartDescription", + FieldKey = "PartDescription", + UiFieldDataType = (int)UiFieldDataType.Text, + SqlValueColumnName = "description" }); FieldDefinitions.Add(new DataListFieldDefinition diff --git a/server/AyaNova/DataList/PartInventoryDataList.cs b/server/AyaNova/DataList/PartInventoryDataList.cs index 66d9490d..f3f40c40 100644 --- a/server/AyaNova/DataList/PartInventoryDataList.cs +++ b/server/AyaNova/DataList/PartInventoryDataList.cs @@ -16,20 +16,10 @@ namespace AyaNova.DataList var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; - DefaultColumns = new List() { "PartPartNumber", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryMinStockLevel", "PartByWarehouseInventoryReorderQuantity", "PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted", "Active" }; - DefaultSortBy = new Dictionary() { { "PartPartNumber", "+" }, { "PartWarehouseName", "+" } }; + DefaultColumns = new List() { "PartName", "PartWarehouseName", "PartInventoryBalance", "PartByWarehouseInventoryMinStockLevel", "PartByWarehouseInventoryReorderQuantity", "PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryQtyOnOrderCommitted", "Active" }; + DefaultSortBy = new Dictionary() { { "PartName", "+" }, { "PartWarehouseName", "+" } }; FieldDefinitions = new List(); - FieldDefinitions.Add(new DataListFieldDefinition - { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", - AType = (int)AyaType.Part, - UiFieldDataType = (int)UiFieldDataType.Text, - SqlIdColumnName = "partid", - SqlValueColumnName = "partnumber" - }); - FieldDefinitions.Add(new DataListFieldDefinition { TKey = "PartName", @@ -40,6 +30,16 @@ namespace AyaNova.DataList SqlValueColumnName = "partname" }); + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PartDescription", + FieldKey = "PartDescription", + AType = (int)AyaType.Part, + UiFieldDataType = (int)UiFieldDataType.Text, + SqlIdColumnName = "partid", + SqlValueColumnName = "partdescription" + }); + FieldDefinitions.Add(new DataListFieldDefinition { TKey = "Active", @@ -162,10 +162,10 @@ namespace AyaNova.DataList FieldDefinitions.Add(new DataListFieldDefinition { - FieldKey = "metapartnumber", + FieldKey = "metapartname", UiFieldDataType = (int)UiFieldDataType.Text, SqlIdColumnName = "partid", - SqlValueColumnName = "partnumber", + SqlValueColumnName = "partname", IsMeta = true }); @@ -194,7 +194,7 @@ namespace AyaNova.DataList //Part criteria if (crit[0] != "0") { - DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metapartnumber" }; + DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metapartname" }; FilterOption.Items.Add(new DataListColumnFilter() { value = crit[0], op = DataListFilterComparisonOperator.Equality }); ret.Add(FilterOption); } diff --git a/server/AyaNova/DataList/PartInventoryRequestDataList.cs b/server/AyaNova/DataList/PartInventoryRequestDataList.cs index ae34a8f3..f6b206ac 100644 --- a/server/AyaNova/DataList/PartInventoryRequestDataList.cs +++ b/server/AyaNova/DataList/PartInventoryRequestDataList.cs @@ -34,7 +34,7 @@ namespace AyaNova.DataList var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; DefaultColumns = new List() { - "PartPartNumber", + "PartName", "WorkOrderSerialNumber", "Customer", "WorkOrderItemPartRequestQuantity", @@ -44,7 +44,7 @@ namespace AyaNova.DataList "WorkOrderItemPartRequestReceived" }; - DefaultSortBy = new Dictionary() { { "PartPartNumber", "+" }, { "WorkOrderSerialNumber", "-" } }; + DefaultSortBy = new Dictionary() { { "PartName", "+" }, { "WorkOrderSerialNumber", "-" } }; FieldDefinitions = new List(); @@ -59,16 +59,6 @@ namespace AyaNova.DataList #region WorkOrderItemPartRequest fields - FieldDefinitions.Add(new DataListFieldDefinition - { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", - UiFieldDataType = (int)UiFieldDataType.Text, - AType = (int)AyaType.Part, - SqlIdColumnName = "apart.id", - SqlValueColumnName = "apart.partnumber" - }); - FieldDefinitions.Add(new DataListFieldDefinition { TKey = "PartName", @@ -79,6 +69,16 @@ namespace AyaNova.DataList SqlValueColumnName = "apart.name" }); + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PartDescription", + FieldKey = "PartDescription", + UiFieldDataType = (int)UiFieldDataType.Text, + AType = (int)AyaType.Part, + SqlIdColumnName = "apart.id", + SqlValueColumnName = "apart.description" + }); + FieldDefinitions.Add(new DataListFieldDefinition { TKey = "WorkOrderItemPartRequestPartWarehouseID", diff --git a/server/AyaNova/DataList/PartInventoryTransactionsDataList.cs b/server/AyaNova/DataList/PartInventoryTransactionsDataList.cs index 63d8e868..75f91830 100644 --- a/server/AyaNova/DataList/PartInventoryTransactionsDataList.cs +++ b/server/AyaNova/DataList/PartInventoryTransactionsDataList.cs @@ -16,7 +16,7 @@ namespace AyaNova.DataList var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; DefaultColumns = new List() { - "PartInventoryTransactionEntryDate", "PartPartNumber", "PartWarehouseName", "PartInventoryTransactionQuantity", + "PartInventoryTransactionEntryDate", "PartName", "PartWarehouseName", "PartInventoryTransactionQuantity", "PartInventoryTransactionDescription", "PartInventoryTransactionSource", "PartInventoryBalance" }; DefaultSortBy = new Dictionary() { { "PartInventoryTransactionEntryDate", "-" } }; @@ -35,12 +35,12 @@ namespace AyaNova.DataList FieldDefinitions.Add(new DataListFieldDefinition { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", + TKey = "PartName", + FieldKey = "PartName", AType = (int)AyaType.Part, UiFieldDataType = (int)UiFieldDataType.Text, SqlIdColumnName = "apart.id", - SqlValueColumnName = "apart.partnumber" + SqlValueColumnName = "apart.name" }); FieldDefinitions.Add(new DataListFieldDefinition @@ -92,10 +92,10 @@ namespace AyaNova.DataList FieldDefinitions.Add(new DataListFieldDefinition { - FieldKey = "metapartnumber", + FieldKey = "metapartname", UiFieldDataType = (int)UiFieldDataType.Text, SqlIdColumnName = "apart.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsMeta = true }); @@ -124,7 +124,7 @@ namespace AyaNova.DataList //Part criteria if (crit[0] != "0") { - DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metapartnumber" }; + DataListFilterOption FilterOption = new DataListFilterOption() { Column = "metapartname" }; FilterOption.Items.Add(new DataListColumnFilter() { value = crit[0], op = DataListFilterComparisonOperator.Equality }); ret.Add(FilterOption); } diff --git a/server/AyaNova/DataList/PartRestockDataList.cs b/server/AyaNova/DataList/PartRestockDataList.cs index a16269bf..d291b9df 100644 --- a/server/AyaNova/DataList/PartRestockDataList.cs +++ b/server/AyaNova/DataList/PartRestockDataList.cs @@ -7,33 +7,37 @@ namespace AyaNova.DataList //NOTE: this object has NO rowid as it's a synthetic list not primarily based on any particular db table public PartRestockDataList(long translationId) { - - // SELECT apart.id AS partid, apartwarehouse.id AS partwarehouseid, apart.partnumber, apartwarehouse.name AS displaywarehouse, " - // + "amanufacturer.id AS manufactureid, amanufacturer.name AS displaymanufacturer, awholesaler.id AS wholesalerid, awholesaler.name AS displaywholesaler, " - // + "aalternativewholesaler.id AS alternativewholesalerid, aalternativewholesaler.name AS displayalternativewholesaler," - // + "apartstocklevel.minimumquantity, vpartinventorynow.balance, COALESCE(vpartsonorder.quantityonorder,0) AS onorderquantity, " - // + "apartstocklevel.minimumquantity - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0)) AS requiredquantity " - + DefaultListAType = AyaType.PartInventoryRestock; - SQLFrom = "FROM vrestockrequired"; + SQLFrom = "FROM viewrestockrequired"; var RoleSet = BizRoles.GetRoleSet(DefaultListAType); AllowedRoles = RoleSet.ReadFullRecord | RoleSet.Change; DefaultColumns = new List() { - "PartPartNumber", "PartWarehouseName", "PartWholesalerID","PartAlternativeWholesalerID","PartManufacturerID", "PartByWarehouseInventoryMinStockLevel", + "PartName", "PartWarehouseName", "PartWholesalerID","PartAlternativeWholesalerID","PartManufacturerID", "PartByWarehouseInventoryMinStockLevel", "PartInventoryBalance", "PartByWarehouseInventoryQuantityOnOrder", "PartByWarehouseInventoryReorderQuantity" }; - DefaultSortBy = new Dictionary() { { "PartPartNumber", "+" }, { "PartWarehouseName", "+" } }; + DefaultSortBy = new Dictionary() { { "PartName", "+" }, { "PartWarehouseName", "+" } }; FieldDefinitions = new List(); FieldDefinitions.Add(new DataListFieldDefinition { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", + TKey = "PartName", + FieldKey = "PartName", AType = (int)AyaType.Part, UiFieldDataType = (int)UiFieldDataType.Text, SqlIdColumnName = "partid", - SqlValueColumnName = "partnumber" + SqlValueColumnName = "partname" + }); + + FieldDefinitions.Add(new DataListFieldDefinition + { + TKey = "PartDescription", + FieldKey = "PartDescription", + AType = (int)AyaType.Part, + UiFieldDataType = (int)UiFieldDataType.Text, + SqlIdColumnName = "partid", + SqlValueColumnName = "partdescription" }); FieldDefinitions.Add(new DataListFieldDefinition diff --git a/server/AyaNova/DataList/QuoteItemPartDataList.cs b/server/AyaNova/DataList/QuoteItemPartDataList.cs index e7369cd7..47f1c119 100644 --- a/server/AyaNova/DataList/QuoteItemPartDataList.cs +++ b/server/AyaNova/DataList/QuoteItemPartDataList.cs @@ -62,7 +62,7 @@ namespace AyaNova.DataList UiFieldDataType = (int)UiFieldDataType.Text, AType = (int)AyaType.QuoteItemPart, SqlIdColumnName = "aquoteitempart.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsRowId = true }); diff --git a/server/AyaNova/DataList/WorkOrderItemPartDataList.cs b/server/AyaNova/DataList/WorkOrderItemPartDataList.cs index a6651efe..d3d8a1f7 100644 --- a/server/AyaNova/DataList/WorkOrderItemPartDataList.cs +++ b/server/AyaNova/DataList/WorkOrderItemPartDataList.cs @@ -61,7 +61,7 @@ namespace AyaNova.DataList UiFieldDataType = (int)UiFieldDataType.Text, AType = (int)AyaType.WorkOrderItemPart, SqlIdColumnName = "aworkorderitempart.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsRowId = true }); diff --git a/server/AyaNova/DataList/WorkOrderItemPartRequestDataList.cs b/server/AyaNova/DataList/WorkOrderItemPartRequestDataList.cs index 339828b7..e9c00dbd 100644 --- a/server/AyaNova/DataList/WorkOrderItemPartRequestDataList.cs +++ b/server/AyaNova/DataList/WorkOrderItemPartRequestDataList.cs @@ -68,7 +68,7 @@ namespace AyaNova.DataList UiFieldDataType = (int)UiFieldDataType.Text, AType = (int)AyaType.WorkOrderItemPartRequest, SqlIdColumnName = "aworkorderitempartrequest.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsRowId = true }); diff --git a/server/AyaNova/PickList/PartPickList.cs b/server/AyaNova/PickList/PartPickList.cs index 04fb5afb..5ab0d1fd 100644 --- a/server/AyaNova/PickList/PartPickList.cs +++ b/server/AyaNova/PickList/PartPickList.cs @@ -17,10 +17,6 @@ namespace AyaNova.PickList dynamic dTemplate = new JArray(); dynamic cm = null; - cm = new JObject(); - cm.fld = "PartPartNumber"; - dTemplate.Add(cm); - cm = new JObject(); cm.fld = "PartName"; dTemplate.Add(cm); @@ -48,20 +44,20 @@ namespace AyaNova.PickList ColumnDefinitions.Add(new AyaPickListFieldDefinition { - TKey = "PartPartNumber", - FieldKey = "PartPartNumber", + TKey = "PartName", + FieldKey = "PartName", ColumnDataType = UiFieldDataType.Text, SqlIdColumnName = "apart.id", - SqlValueColumnName = "apart.partnumber", + SqlValueColumnName = "apart.name", IsRowId = true }); ColumnDefinitions.Add(new AyaPickListFieldDefinition { - TKey = "PartName", - FieldKey = "PartName", + TKey = "PartDescription", + FieldKey = "PartDescription", ColumnDataType = UiFieldDataType.Text, - SqlValueColumnName = "apart.name" + SqlValueColumnName = "apart.description" }); ColumnDefinitions.Add(new AyaPickListFieldDefinition diff --git a/server/AyaNova/biz/DataListSavedFilterBiz.cs b/server/AyaNova/biz/DataListSavedFilterBiz.cs index bd9cc396..56187b80 100644 --- a/server/AyaNova/biz/DataListSavedFilterBiz.cs +++ b/server/AyaNova/biz/DataListSavedFilterBiz.cs @@ -112,7 +112,7 @@ namespace AyaNova.Biz d.DefaultFilter = true; d.Public = false; d.UserId = UserId; - d.Filter = "[]";//empty array is default becuase it would be filter:[{column:"PartPartNumber",any:true/false,items:[{op: "=",value: "400735"}]}] + d.Filter = "[]";//empty array is default becuase it would be filter:[{column:"PartName",any:true/false,items:[{op: "=",value: "400735"}]}] await ct.DataListSavedFilter.AddAsync(d); await ct.SaveChangesAsync(); } diff --git a/server/AyaNova/biz/FormFieldReference.cs b/server/AyaNova/biz/FormFieldReference.cs index 65744dee..8fc2979f 100644 --- a/server/AyaNova/biz/FormFieldReference.cs +++ b/server/AyaNova/biz/FormFieldReference.cs @@ -384,6 +384,7 @@ namespace AyaNova.Biz { List l = new List(); l.Add(new FormField { TKey = "PartName", FieldKey = "Name" }); + l.Add(new FormField { TKey = "PartDescription", FieldKey = "Description" }); l.Add(new FormField { TKey = "UnitOfMeasure", FieldKey = "UnitOfMeasure" }); l.Add(new FormField { TKey = "PartUPC", FieldKey = "UPC" }); l.Add(new FormField { TKey = "PartManufacturerID", FieldKey = "ManufacturerID" }); diff --git a/server/AyaNova/biz/PMBiz.cs b/server/AyaNova/biz/PMBiz.cs index 1ce0c95f..cfd0f5f4 100644 --- a/server/AyaNova/biz/PMBiz.cs +++ b/server/AyaNova/biz/PMBiz.cs @@ -3078,7 +3078,7 @@ namespace AyaNova.Biz else return;//this should never happen but this is insurance in case it does - o.PartViz = part.PartNumber; + o.PartDescriptionViz = part.Description; o.PartNameViz = part.Name; o.UpcViz = part.UPC; @@ -4666,7 +4666,7 @@ namespace AyaNova.Biz //Get the translations for this user List transl = new List(); - transl.Add("PartPartNumber"); + transl.Add("PartName"); transl.Add("PartWarehouse"); transl.Add("QuantityRequired"); var Trans = await TranslationBiz.GetSubsetForUserStaticAsync(transl, sub.UserId); @@ -4737,10 +4737,10 @@ namespace AyaNova.Biz dBalance = CurrentInventory.Balance; if (dBalance < i.QuantityRequired) { - var part = await ct.Part.AsNoTracking().Where(x => x.Id == i.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); + var part = await ct.Part.AsNoTracking().Where(x => x.Id == i.PartId).Select(x => x.Name).FirstOrDefaultAsync(); var whs = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == i.WarehouseId).Select(x => x.Name).FirstOrDefaultAsync(); var qty = (i.QuantityRequired - dBalance).ToString("G29", System.Globalization.CultureInfo.InvariantCulture); - sb.Append($"{Trans["PartPartNumber"]}: {part}, {Trans["PartWarehouse"]}: {whs}, {Trans["QuantityRequired"]}: {qty}\n"); + sb.Append($"{Trans["PartName"]}: {part}, {Trans["PartWarehouse"]}: {whs}, {Trans["QuantityRequired"]}: {qty}\n"); } } if (sb.Length > 0) diff --git a/server/AyaNova/biz/PartAssemblyBiz.cs b/server/AyaNova/biz/PartAssemblyBiz.cs index 408d7693..98daaaef 100644 --- a/server/AyaNova/biz/PartAssemblyBiz.cs +++ b/server/AyaNova/biz/PartAssemblyBiz.cs @@ -354,7 +354,7 @@ namespace AyaNova.Biz private async Task PopulateVizFields(PartAssembly pa) { foreach (PartAssemblyItem o in pa.Items) - o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); + o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.Name).FirstOrDefaultAsync(); } diff --git a/server/AyaNova/biz/PartBiz.cs b/server/AyaNova/biz/PartBiz.cs index 97ec70dd..a570d696 100644 --- a/server/AyaNova/biz/PartBiz.cs +++ b/server/AyaNova/biz/PartBiz.cs @@ -409,7 +409,7 @@ namespace AyaNova.Biz if (obj != null) searchParams.AddText(obj.Notes) .AddText(obj.Name) - .AddText(obj.PartNumber) + .AddText(obj.Description) .AddText(obj.ManufacturerNumber) .AddText(obj.WholeSalerNumber) .AddText(obj.AlternativeWholeSalerNumber) @@ -431,19 +431,19 @@ namespace AyaNova.Biz bool isNew = currentObj == null; - //PartNumber required - if (string.IsNullOrWhiteSpace(proposedObj.PartNumber)) - AddError(ApiErrorCode.VALIDATION_REQUIRED, "PartNumber"); + //name required + if (string.IsNullOrWhiteSpace(proposedObj.Name)) + AddError(ApiErrorCode.VALIDATION_REQUIRED, "PartName"); - //If PartNumber is otherwise OK, check that PartNumber is unique - if (!PropertyHasErrors("PartNumber")) + //If name is otherwise OK, check that Name is unique + if (!PropertyHasErrors("Name")) { //Use Any command is efficient way to check existance, it doesn't return the record, just a true or false - if (await ct.Part.AnyAsync(m => m.PartNumber == proposedObj.PartNumber && m.Id != proposedObj.Id)) + if (await ct.Part.AnyAsync(m => m.Name == proposedObj.Name && m.Id != proposedObj.Id)) { - AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "PartNumber"); + AddError(ApiErrorCode.VALIDATION_NOT_UNIQUE, "PartName"); } } diff --git a/server/AyaNova/biz/PartInventoryBiz.cs b/server/AyaNova/biz/PartInventoryBiz.cs index 03a86de2..a879b9df 100644 --- a/server/AyaNova/biz/PartInventoryBiz.cs +++ b/server/AyaNova/biz/PartInventoryBiz.cs @@ -293,7 +293,7 @@ namespace AyaNova.Biz //populate viz fields from provided object private async Task PopulateVizFields(PartInventory o, List ayaTypesEnumList, System.Data.Common.DbCommand cmd) { - o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); + o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.Name).FirstOrDefaultAsync(); o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync(); if (o.SourceType != null) o.SourceTypeViz = ayaTypesEnumList.Where(x => x.Id == (long)o.SourceType).Select(x => x.Name).First(); diff --git a/server/AyaNova/biz/PartInventoryDataListBiz.cs b/server/AyaNova/biz/PartInventoryDataListBiz.cs index f5b5dd4e..ebc59d0f 100644 --- a/server/AyaNova/biz/PartInventoryDataListBiz.cs +++ b/server/AyaNova/biz/PartInventoryDataListBiz.cs @@ -68,17 +68,7 @@ namespace AyaNova.Biz return ReportData; } - // //populate viz fields from provided object - // private async Task PopulateVizFields(VPartInventoryList o, List ayaTypesEnumList, System.Data.Common.DbCommand cmd) - // { - // o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); - // o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync(); - // if (o.SourceType != null) - // o.SourceTypeViz = ayaTypesEnumList.Where(x => x.Id == (long)o.SourceType).Select(x => x.Name).First(); - // if (o.SourceType != null && o.SourceId != null) - // o.SourceViz = BizObjectNameFetcherDirect.Name((AyaType)o.SourceType, (long)o.SourceId, cmd); - - // } + //////////////////////////////////////////////////////////////////////////////////////////////// // IMPORT EXPORT diff --git a/server/AyaNova/biz/PartInventoryRequestDataListBiz.cs b/server/AyaNova/biz/PartInventoryRequestDataListBiz.cs index 39680541..714ad1c5 100644 --- a/server/AyaNova/biz/PartInventoryRequestDataListBiz.cs +++ b/server/AyaNova/biz/PartInventoryRequestDataListBiz.cs @@ -68,17 +68,7 @@ namespace AyaNova.Biz return ReportData; } - // //populate viz fields from provided object - // private async Task PopulateVizFields(VPartInventoryList o, List ayaTypesEnumList, System.Data.Common.DbCommand cmd) - // { - // o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); - // o.PartWarehouseViz = await ct.PartWarehouse.AsNoTracking().Where(x => x.Id == o.PartWarehouseId).Select(x => x.Name).FirstOrDefaultAsync(); - // if (o.SourceType != null) - // o.SourceTypeViz = ayaTypesEnumList.Where(x => x.Id == (long)o.SourceType).Select(x => x.Name).First(); - // if (o.SourceType != null && o.SourceId != null) - // o.SourceViz = BizObjectNameFetcherDirect.Name((AyaType)o.SourceType, (long)o.SourceId, cmd); - // } //////////////////////////////////////////////////////////////////////////////////////////////// // IMPORT EXPORT diff --git a/server/AyaNova/biz/PurchaseOrderBiz.cs b/server/AyaNova/biz/PurchaseOrderBiz.cs index 46319031..a6abe49a 100644 --- a/server/AyaNova/biz/PurchaseOrderBiz.cs +++ b/server/AyaNova/biz/PurchaseOrderBiz.cs @@ -180,8 +180,8 @@ namespace AyaNova.Biz { - var partInfo = await ct.Part.AsNoTracking().Where(x => x.Id == item.PartId).Select(x => new { partViz = x.PartNumber, partNameViz = x.Name, partunitofmeasureviz = x.UnitOfMeasure, partmanufacturernumber = x.ManufacturerNumber }).FirstOrDefaultAsync(); - item.PartViz = partInfo.partViz; + var partInfo = await ct.Part.AsNoTracking().Where(x => x.Id == item.PartId).Select(x => new { partViz = x.Description, partNameViz = x.Name, partunitofmeasureviz = x.UnitOfMeasure, partmanufacturernumber = x.ManufacturerNumber }).FirstOrDefaultAsync(); + item.PartDescriptionViz = partInfo.partViz; item.PartNameViz = partInfo.partNameViz; item.PartUnitOfMeasureViz = partInfo.partunitofmeasureviz; item.PartManufacturerNumberViz = partInfo.partmanufacturernumber; diff --git a/server/AyaNova/biz/QuoteBiz.cs b/server/AyaNova/biz/QuoteBiz.cs index d5df3526..367cd191 100644 --- a/server/AyaNova/biz/QuoteBiz.cs +++ b/server/AyaNova/biz/QuoteBiz.cs @@ -3339,7 +3339,7 @@ namespace AyaNova.Biz else return;//this should never happen but this is insurance in case it does - o.PartViz = part.PartNumber; + o.PartDescriptionViz = part.Description; o.PartNameViz = part.Name; o.UpcViz = part.UPC; diff --git a/server/AyaNova/biz/TaskGroupBiz.cs b/server/AyaNova/biz/TaskGroupBiz.cs index 75fe6cf1..9f8fa3c1 100644 --- a/server/AyaNova/biz/TaskGroupBiz.cs +++ b/server/AyaNova/biz/TaskGroupBiz.cs @@ -310,13 +310,7 @@ namespace AyaNova.Biz return ReportData; } - // //populate viz fields from provided object - // private async Task PopulateVizFields(TaskGroup pa) - // { - // foreach (TaskGroupItem o in pa.Items) - // o.PartViz = await ct.Part.AsNoTracking().Where(x => x.Id == o.PartId).Select(x => x.PartNumber).FirstOrDefaultAsync(); - // } - + //////////////////////////////////////////////////////////////////////////////////////////////// // IMPORT EXPORT diff --git a/server/AyaNova/biz/WorkOrderBiz.cs b/server/AyaNova/biz/WorkOrderBiz.cs index 4662e9bd..b63297d6 100644 --- a/server/AyaNova/biz/WorkOrderBiz.cs +++ b/server/AyaNova/biz/WorkOrderBiz.cs @@ -3985,8 +3985,7 @@ namespace AyaNova.Biz part = await ct.Part.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.PartId); else return;//this should never happen but this is insurance in case it does - - o.PartViz = part.PartNumber; + o.PartNameViz = part.Name; o.UpcViz = part.UPC; @@ -4687,7 +4686,7 @@ namespace AyaNova.Biz Part part = null; if (o.PartId != 0) part = await ct.Part.AsNoTracking().FirstOrDefaultAsync(x => x.Id == o.PartId); - o.PartViz = part.PartNumber; + o.PartViz = part.Name; o.UpcViz = part.UPC; PurchaseOrder po = null; diff --git a/server/AyaNova/models/DataListColumnView.cs b/server/AyaNova/models/DataListColumnView.cs index fdc1fd80..527dc27a 100644 --- a/server/AyaNova/models/DataListColumnView.cs +++ b/server/AyaNova/models/DataListColumnView.cs @@ -16,7 +16,7 @@ namespace AyaNova.Models [Required, MaxLength(255)] public string ListKey { get; set; }//max 255 characters ascii set - public string Columns { get; set; }//JSON serialized Column array: columns:["PartInventoryTransactionEntryDate","PartPartNumber","PartWarehouseName","PartInventoryTransactionQuantity","PartInventoryTransactionDescription","PartInventoryTransactionSource","PartInventoryBalance"] + public string Columns { get; set; }//JSON serialized Column array: columns:["PartInventoryTransactionEntryDate","PartName","PartWarehouseName","PartInventoryTransactionQuantity","PartInventoryTransactionDescription","PartInventoryTransactionSource","PartInventoryBalance"] public string Sort { get; set; }//JSON serialized SortBy Dictionary: sortBy:[{"PartInventoryTransactionEntryDate":"-"}],//All sorted columns here as keyvalue pairs value is a string of "+" for ascending "-" for descending and are IN ORDER of how to be sorted diff --git a/server/AyaNova/models/DataListFilterOption.cs b/server/AyaNova/models/DataListFilterOption.cs index f0deef97..24feeac3 100644 --- a/server/AyaNova/models/DataListFilterOption.cs +++ b/server/AyaNova/models/DataListFilterOption.cs @@ -14,9 +14,9 @@ namespace AyaNova.Models } /* - columns:["PartInventoryTransactionEntryDate","PartPartNumber","PartWarehouseName","PartInventoryTransactionQuantity","PartInventoryTransactionDescription","PartInventoryTransactionSource","PartInventoryBalance"] + columns:["PartInventoryTransactionEntryDate","PartName","PartWarehouseName","PartInventoryTransactionQuantity","PartInventoryTransactionDescription","PartInventoryTransactionSource","PartInventoryBalance"] sortBy:[{"PartInventoryTransactionEntryDate":"-"}],//All sorted columns here as keyvalue pairs value is a string of "+" for ascending "-" for descending and are IN ORDER of how to be sorted - filter:[{column:"PartPartNumber",any:true/false,items:[{op: "=",value: "400735"}]}], + filter:[{column:"PartName",any:true/false,items:[{op: "=",value: "400735"}]}], clientCriteria:"2" //could be anything here that makes sense to the list, in this case an example customer id for customernotedatalist columns are represented in a higher level object DataListTableOptions diff --git a/server/AyaNova/models/PMItemPart.cs b/server/AyaNova/models/PMItemPart.cs index 1f40df83..1f87ec59 100644 --- a/server/AyaNova/models/PMItemPart.cs +++ b/server/AyaNova/models/PMItemPart.cs @@ -16,7 +16,7 @@ namespace AyaNova.Models [Required] public long PartId { get; set; } [NotMapped] - public string PartViz { get; set; } + public string PartDescriptionViz { get; set; } [NotMapped] public string PartNameViz { get; set; } [NotMapped] diff --git a/server/AyaNova/models/Part.cs b/server/AyaNova/models/Part.cs index 8e541beb..bd00e9aa 100644 --- a/server/AyaNova/models/Part.cs +++ b/server/AyaNova/models/Part.cs @@ -9,22 +9,23 @@ namespace AyaNova.Models { //NOTE: Any non required field (nullable in DB) sb nullable here, i.e. decimal? not decimal, //otherwise the server will call it an invalid record if the field isn't sent from client -//about to make major changes commit trigger here + //about to make major changes commit trigger here public class Part : ICoreBizObjectModel { public long Id { get; set; } public uint Concurrency { get; set; } + [Required] + public string Name { get; set; }//was partnumber in v7 - public string Name { get; set; } [Required] public bool Active { get; set; } + public string Description { get; set; }//was "name" in v7 public string Notes { get; set; } public string Wiki { get; set; } public string CustomFields { get; set; } public List Tags { get; set; } - [Required] - public string PartNumber { get; set; } + public long? ManufacturerId { get; set; } [NotMapped] public string ManufacturerViz { get; set; } diff --git a/server/AyaNova/models/PurchaseOrderItem.cs b/server/AyaNova/models/PurchaseOrderItem.cs index 79eb4fe1..ff1fcb1b 100644 --- a/server/AyaNova/models/PurchaseOrderItem.cs +++ b/server/AyaNova/models/PurchaseOrderItem.cs @@ -37,11 +37,12 @@ namespace AyaNova.Models //server populated fields not db fields - [NotMapped] - public string PartViz { get; set; } + [NotMapped] public string PartNameViz { get; set; } [NotMapped] + public string PartDescriptionViz { get; set; } + [NotMapped] public string PartManufacturerNumberViz { get; set; } diff --git a/server/AyaNova/models/QuoteItemPart.cs b/server/AyaNova/models/QuoteItemPart.cs index aeb52dcf..c5368c9d 100644 --- a/server/AyaNova/models/QuoteItemPart.cs +++ b/server/AyaNova/models/QuoteItemPart.cs @@ -16,7 +16,7 @@ namespace AyaNova.Models [Required] public long PartId { get; set; } [NotMapped] - public string PartViz { get; set; } + public string PartDescriptionViz { get; set; } [NotMapped] public string PartNameViz { get; set; } [NotMapped] diff --git a/server/AyaNova/models/ViewPartInventoryList.cs b/server/AyaNova/models/ViewPartInventoryList.cs index f5bc0b52..efcbb4d1 100644 --- a/server/AyaNova/models/ViewPartInventoryList.cs +++ b/server/AyaNova/models/ViewPartInventoryList.cs @@ -8,7 +8,7 @@ namespace AyaNova.Models public class ViewPartInventoryList { public long PartId { get; set; } - public string PartNumber { get; set; } + public string PartDescription { get; set; } public string PartName { get; set; } public bool PartActive { get; set; } public decimal PartCost { get; set; } @@ -32,12 +32,3 @@ namespace AyaNova.Models }//eoc }//eons - -/* -vpartinventorylist AS select apart.id as partid, apart.partnumber, apart.name as partname, apart.active as partactive, apart.cost as partcost, apart.retail as partretail," -+"apartwarehouse.id as partwarehouseid, apartwarehouse.name as partwarehousename, awholesaler.name as wholesalername, awholesaler.id as wholesalerid, " -+"aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,vpartsonorder.quantityonorder as onorderqty, " -+"vpartsonordercommitted.quantityonordercommitted as onordercommittedqty,apartstocklevel.minimumquantity as restockminqty, " -+"GREATEST( COALESCE(apartstocklevel.minimumquantity, 0) - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorder.quantityonorder, 0) - COALESCE(vpartsonordercommitted.quantityonordercommitted, 0)) ,0) AS reorderqty," -+"vpartinventorynow.id as partinventoryid, vpartinventorynow.description as partinventorydescription " -*/ \ No newline at end of file diff --git a/server/AyaNova/models/ViewPartInventoryRequestList.cs b/server/AyaNova/models/ViewPartInventoryRequestList.cs index dcfbe190..c00435a7 100644 --- a/server/AyaNova/models/ViewPartInventoryRequestList.cs +++ b/server/AyaNova/models/ViewPartInventoryRequestList.cs @@ -17,7 +17,7 @@ namespace AyaNova.Models public long? WoStatusId { get; set; } public string StatusColor { get; set; } public string StatusName { get; set; } - public string PartNumber { get; set; } + public string PartDescription { get; set; } public string PartName { get; set; } public string PartWarehouseName { get; set; } public long? WholesalerId { get; set; } diff --git a/server/AyaNova/models/ViewRestockRequired.cs b/server/AyaNova/models/ViewRestockRequired.cs index 37c3e096..138c5861 100644 --- a/server/AyaNova/models/ViewRestockRequired.cs +++ b/server/AyaNova/models/ViewRestockRequired.cs @@ -7,7 +7,8 @@ namespace AyaNova.Models { public long PartId { get; set; } public long PartWarehouseId { get; set; } - public string PartNumber { get; set; } + public string PartName { get; set; } + public string PartDescription { get; set; } public string DisplayWarehouse { get; set; } public long ManufacturerId { get; set; } public string DisplayManufacturer { get; set; } @@ -24,11 +25,3 @@ namespace AyaNova.Models }//eons -/* -SELECT apart.id AS partid, apartwarehouse.id AS partwarehouseid, apart.partnumber, apartwarehouse.name AS displaywarehouse, " - + "amanufacturer.id AS manufacturerid, amanufacturer.name AS displaymanufacturer, awholesaler.id AS wholesalerid, awholesaler.name AS displaywholesaler, " - + "aalternativewholesaler.id AS alternativewholesalerid, aalternativewholesaler.name AS displayalternativewholesaler," - + "apartstocklevel.minimumquantity, vpartinventorynow.balance, COALESCE(vpartsonorderuncommitted.quantityonorder,0) AS onorderquantity, " - + "apartstocklevel.minimumquantity - (COALESCE(vpartinventorynow.balance, 0) + COALESCE(vpartsonorderuncommitted.quantityonorder, 0)) AS requiredquantity " - -*/ \ No newline at end of file diff --git a/server/AyaNova/models/ViewUnfulfilledPartRequestList.cs b/server/AyaNova/models/ViewUnfulfilledPartRequestList.cs index 0452cc1b..778f4941 100644 --- a/server/AyaNova/models/ViewUnfulfilledPartRequestList.cs +++ b/server/AyaNova/models/ViewUnfulfilledPartRequestList.cs @@ -10,7 +10,7 @@ namespace AyaNova.Models public long? PartWarehouseId { get; set; } public decimal Quantity { get; set; } public long Serial { get; set; } - public string PartNumber { get; set; } + public string PartDescription { get; set; } public string PartName { get; set; } public string PartWarehouseName { get; set; } public long? WholesalerId { get; set; } diff --git a/server/AyaNova/resource/de.json b/server/AyaNova/resource/de.json index 18750bf5..9ffe5084 100644 --- a/server/AyaNova/resource/de.json +++ b/server/AyaNova/resource/de.json @@ -607,7 +607,7 @@ "PartManufacturerNumber": "Herstellernummer", "PartName": "Teil - Name", "PartNotes": "Anmerkungen", - "PartPartNumber": "Teilenummer", + "PartName": "Teilenummer", "PartRetail": "Einzelhandel", "PartCost": "Kosten", "PartTrackSerialNumber": "Seriennummer verfolgen", diff --git a/server/AyaNova/resource/en.json b/server/AyaNova/resource/en.json index 71ce73a6..eaa60181 100644 --- a/server/AyaNova/resource/en.json +++ b/server/AyaNova/resource/en.json @@ -607,7 +607,7 @@ "PartManufacturerNumber": "Manufacturer Number", "PartName": "Part Name", "PartNotes": "Notes", - "PartPartNumber": "Part Number", + "PartDescription": "Part Number", "PartRetail": "Retail", "PartCost": "Cost", "PartTrackSerialNumber": "Track Serial Number", diff --git a/server/AyaNova/resource/es.json b/server/AyaNova/resource/es.json index a9e58ffa..0a6aaef1 100644 --- a/server/AyaNova/resource/es.json +++ b/server/AyaNova/resource/es.json @@ -606,7 +606,7 @@ "PartManufacturerNumber": "Número de fabricante", "PartName": "Nombre de la pieza", "PartNotes": "Notas", - "PartPartNumber": "Número de pieza", + "PartName": "Número de pieza", "PartRetail": "Minorista", "PartCost": "Coste", "PartTrackSerialNumber": "Seguimiento número de serie", diff --git a/server/AyaNova/resource/fr.json b/server/AyaNova/resource/fr.json index 7c7a94a4..aaca6f8e 100644 --- a/server/AyaNova/resource/fr.json +++ b/server/AyaNova/resource/fr.json @@ -607,7 +607,7 @@ "PartManufacturerNumber": "Numéro de fabricant", "PartName": "Nom de pièce", "PartNotes": "Remarques", - "PartPartNumber": "Numéro de pièce", + "PartName": "Numéro de pièce", "PartRetail": "Détail", "PartCost": "Coût", "PartTrackSerialNumber": "Numéro de série de suivi", diff --git a/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form Grouped By Tag.ayrt b/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form Grouped By Tag.ayrt index 2c275dab..75f43cce 100644 --- a/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form Grouped By Tag.ayrt +++ b/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form Grouped By Tag.ayrt @@ -1 +1 @@ -{"Name":"Example Inventory Reconciliation Form Grouped By Tag","Active":true,"Notes":"Example custom Prepare that groups by the Tags - nothing shows if no tags\nRecommended to use datalist Filter \"Contains\" to first filter by the Tag(s) desired","Roles":49258,"AType":90,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t \n \t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n \t\t\n\n\t\t\t \n\t\t\t\t{{#each ayReportData}} \n\t\t\t\t \n\t\t\t \t\t\n\t\t\t \t\t\n \t\t\n\t\t\t\t \t\t{{#each items}}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{{/each}}\n\t\t\t\t\n\t\t\t \t\n \t\t\n\t\t\t\t{{/each}}\n\t\t\t \n\n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t \n \n\t\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n font-size: 9pt;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 11pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(ayData) {\n\n\n //Group by all tags no filter\n ayData.ayReportData = ayGroupByTag(ayData.ayReportData);\n\n //Group by filtered tags that contain 'zone'\n //ayData.ayReportData = ayGroupByTag(ayData.ayReportData, 'zone');\n\n return ayData;\n}\n\nfunction ayGroupByTag(reportDataArray, tagContains) {\n //array to hold grouped data\n const ret = [];\n const containsQuery = tagContains != null && tagContains != '';\n\n//NOTE that tags are referred to as PartTags so this custom Prepare must reference PartTags, not Tags\n\n //iterate through the raw reprot data \n for (let i = 0; i < reportDataArray.length; i++) {\n //get a reference to each object to save typing\n let o = reportDataArray[i];\n //don't bother with any that don't have tags at all\n if (o.PartTags && o.PartTags.length) {\n //loop through all tags for this record\n o.PartTags.forEach(t => {//t=each tag\n //if not a contains query just process it, if it is a contains query then only process if tag contains tagContains\n if (!containsQuery || t.includes(tagContains)) {\n let groupObject = ret.find(z => z.group == t);\n if (groupObject != undefined) {\n //there is already a matching group in the return array so just push this raw report data record into it\n groupObject.items.push(o);\n //update the count for this group's items\n groupObject.count++;\n } else {\n //No group yet, so start a new one in the ret array and push this raw report data record\n ret.push({ group: t, items: [o], count: 1 });\n }\n }\n })\n }\n }\n\n //Sort based on the group name in a locale aware manner\n ret.sort(function (a, b) {\n return a.group.localeCompare(b.group);\n });\n return ret;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file +{"Name":"Example Inventory Reconciliation Form Grouped By Tag","Active":true,"Notes":"Example custom Prepare that groups by the Tags - nothing shows if no tags\nRecommended to use datalist Filter \"Contains\" to first filter by the Tag(s) desired","Roles":49258,"AType":90,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t
 
My Company NameInventory Reconciliation Form
 
WarehouseCategoryPart Number and NameRetailOn HandActual CountDifference + / (-)
Parts tagged with: {{group}}# of parts for {{group}}: {{count}}
{{PartWarehouseName}}{{PartTags}} {{PartNumber}} {{PartName}}{{ayCurrency PartRetail}}{{OnHandQty}}
 
 
Count By:__________________________________________Count Date:__________________________________________
 
 
Signature:__________________________________________
\n \t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n \t\t\n\n\t\t\t \n\t\t\t\t{{#each ayReportData}} \n\t\t\t\t \n\t\t\t \t\t\n\t\t\t \t\t\n \t\t\n\t\t\t\t \t\t{{#each items}}\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t{{/each}}\n\t\t\t\t\n\t\t\t \t\n \t\t\n\t\t\t\t{{/each}}\n\t\t\t \n\n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t \n \n\t\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n font-size: 9pt;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 11pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(ayData) {\n\n\n //Group by all tags no filter\n ayData.ayReportData = ayGroupByTag(ayData.ayReportData);\n\n //Group by filtered tags that contain 'zone'\n //ayData.ayReportData = ayGroupByTag(ayData.ayReportData, 'zone');\n\n return ayData;\n}\n\nfunction ayGroupByTag(reportDataArray, tagContains) {\n //array to hold grouped data\n const ret = [];\n const containsQuery = tagContains != null && tagContains != '';\n\n//NOTE that tags are referred to as PartTags so this custom Prepare must reference PartTags, not Tags\n\n //iterate through the raw reprot data \n for (let i = 0; i < reportDataArray.length; i++) {\n //get a reference to each object to save typing\n let o = reportDataArray[i];\n //don't bother with any that don't have tags at all\n if (o.PartTags && o.PartTags.length) {\n //loop through all tags for this record\n o.PartTags.forEach(t => {//t=each tag\n //if not a contains query just process it, if it is a contains query then only process if tag contains tagContains\n if (!containsQuery || t.includes(tagContains)) {\n let groupObject = ret.find(z => z.group == t);\n if (groupObject != undefined) {\n //there is already a matching group in the return array so just push this raw report data record into it\n groupObject.items.push(o);\n //update the count for this group's items\n groupObject.count++;\n } else {\n //No group yet, so start a new one in the ret array and push this raw report data record\n ret.push({ group: t, items: [o], count: 1 });\n }\n }\n })\n }\n }\n\n //Sort based on the group name in a locale aware manner\n ret.sort(function (a, b) {\n return a.group.localeCompare(b.group);\n });\n return ret;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file diff --git a/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form.ayrt b/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form.ayrt index 109a6bc5..92c4526a 100644 --- a/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form.ayrt +++ b/server/AyaNova/resource/rpt/stock-report-templates/Example Inventory Reconciliation Form.ayrt @@ -1 +1 @@ -{"Name":"Example Inventory Reconciliation Form","Active":true,"Notes":"","Roles":49258,"AType":90,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t
 
My Company NameInventory Reconciliation Form
 
WarehouseCategoryPart Number and NameRetailOn HandActual CountDifference + / (-)
Parts tagged with: {{group}}# of parts for {{group}}: {{count}}
{{PartWarehouseName}}{{PartTags}} {{PartName}}{{ayCurrency PartRetail}}{{OnHandQty}}
 
 
Count By:__________________________________________Count Date:__________________________________________
 
 
Signature:__________________________________________
\n \t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n \t\t\n\n\t\t\t \n\t\t\t\t {{#each ayReportData}} \n\t\t\t\t \n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n\t\t\t\t {{/each}}\n\t\t\t \n\n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t \n \n\t\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n font-size: 9pt;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 11pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file +{"Name":"Example Inventory Reconciliation Form","Active":true,"Notes":"","Roles":49258,"AType":90,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t
 
My Company NameInventory Reconciliation Form
 
WarehouseCategoryPart Number and NameOn HandActual CountDifference + / (-)
{{PartWarehouseName}}{{PartTags}} {{PartNumber}} {{PartName}}{{OnHandQty}}
 
Count By:__________________________________________Count Date:__________________________________________
 
 
Signature:__________________________________________
\n \t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n \t\t\n\n\t\t\t \n\t\t\t\t {{#each ayReportData}} \n\t\t\t\t \n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t \n\t\t\t\t {{/each}}\n\t\t\t \n\n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t \n \n\t\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n font-size: 9pt;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 11pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n return reportData;\n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file diff --git a/server/AyaNova/resource/rpt/stock-report-templates/Example Parts grouped by Tags with optional filter by contains.ayrt b/server/AyaNova/resource/rpt/stock-report-templates/Example Parts grouped by Tags with optional filter by contains.ayrt index 6d9c80c4..72d01d28 100644 --- a/server/AyaNova/resource/rpt/stock-report-templates/Example Parts grouped by Tags with optional filter by contains.ayrt +++ b/server/AyaNova/resource/rpt/stock-report-templates/Example Parts grouped by Tags with optional filter by contains.ayrt @@ -1 +1 @@ -{"Name":"Example Parts grouped by Tags with optional filter by contains","Active":true,"Notes":"Example custom Prepare that groups by the Tags - nothing shows if no tags\nRecommended to use datalist Filter \"Contains\" to first filter by the Tag(s) desired","Roles":124927,"AType":20,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t
 
My Company NameInventory Reconciliation Form
 
WarehouseCategoryPart Number and NameOn HandActual CountDifference + / (-)
{{PartWarehouseName}}{{PartTags}} {{PartName}}{{OnHandQty}}
 
Count By:__________________________________________Count Date:__________________________________________
 
 
Signature:__________________________________________
\n \t\t\n\n \n \n \n \n \t\n \n \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t \n \n \n \t\t\n \t\n \t\t\n \n \n {{#each ayReportData}}\n \n \n\t\t\t \n\t\t\t \n \n\t\t {{#each items}}\n\t\t \n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t \n \n\t\t\t{{/each}}\n\t\t\t\n\t\t\t \n \n \t {{/each}}\n \n
Parts by Tag
 
TagPartNumberNameManufacturerVizCostRetail
 
{{group}}# of parts for {{group}}: {{count}}
 {{PartNumber}}{{Name}}{{ManufacturerViz}}{{ayCurrency Cost}}{{ayCurrency Retail}}
 
\n\n\n \n\t
\n\n","Style":".singlePage\r\n{\r\npage-break-after: always;\r\n}\r\n\r\ntable { \r\n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\r\n border-collapse: collapse;\r\n white-space: pre-wrap;\r\n width: 100%;\r\n table-layout: fixed;\r\n }\r\n\r\nthead {\r\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\r\n}\r\n\r\n.heading {\r\n border-style: solid;\r\n border-width: 1pt;\r\n border-color: #e8e5e5; \r\n margin: 10pt;\r\n background-color: #e8e5e5;\r\n padding: 5pt; \r\n font-size: 14pt; \r\n text-align: center;\r\n} \r\n\r\n.bodyhead {\r\n border-style: solid;\r\n border-width: 1pt;\r\n border-color: #e8e5e5; \r\n margin: 10pt;\r\n background-color: #e8e5e5;\r\n font-size: 12pt; \r\n}\r\n\r\n\r\n.rightlean {\r\n text-align: right;\r\n}\r\n.leftlean {\r\n text-align: left;\r\n}\r\n.centerlean {\r\n text-align: center;\r\n}\r\ntbody tr:nth-child(even) {\r\n font-size: 9pt;\r\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\r\n height: 50px;\r\n overflow-wrap: break-word;\r\n} \r\ntbody tr:nth-child(odd) {\r\n font-size: 9pt;\r\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\r\n height: 50px;\r\n overflow-wrap: break-word;\r\n} \r\n\r\n\r\ntfoot {\r\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\r\n page-break-inside: avoid;\r\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\r\n \r\n bottom: 0;\r\n width: 100%;\r\n border-top: 1px solid black; \r\n}\r\n\r\n\r\n.fontgreen {\r\n color: green;\r\n}\r\n.fontblue {\r\n color: blue;\r\n}\r\n.fontred {\r\n color:red;\r\n}\r\n\r\n","JsPrerender":"async function ayPrepareData(ayData) {\n\n\n //Group by all tags no filter\n ayData.ayReportData = ayGroupByTag(ayData.ayReportData);\n\n //Group by filtered tags that contain 'zone'\n //ayData.ayReportData = ayGroupByTag(ayData.ayReportData, 'zone');\n\n return ayData;\n}\n\nfunction ayGroupByTag(reportDataArray, tagContains) {\n //array to hold grouped data\n const ret = [];\n const containsQuery = tagContains != null && tagContains != '';\n\n //iterate through the raw reprot data \n for (let i = 0; i < reportDataArray.length; i++) {\n //get a reference to each object to save typing\n let o = reportDataArray[i];\n //don't bother with any that don't have tags at all\n if (o.Tags && o.Tags.length) {\n //loop through all tags for this record\n o.Tags.forEach(t => {//t=each tag\n //if not a contains query just process it, if it is a contains query then only process if tag contains tagContains\n if (!containsQuery || t.includes(tagContains)) {\n let groupObject = ret.find(z => z.group == t);\n if (groupObject != undefined) {\n //there is already a matching group in the return array so just push this raw report data record into it\n groupObject.items.push(o);\n //update the count for this group's items\n groupObject.count++;\n } else {\n //No group yet, so start a new one in the ret array and push this raw report data record\n ret.push({ group: t, items: [o], count: 1 });\n }\n }\n })\n }\n }\n\n //Sort based on the group name in a locale aware manner\n ret.sort(function (a, b) {\n return a.group.localeCompare(b.group);\n });\n return ret;\n}","JsHelpers":"","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file +{"Name":"Example Parts grouped by Tags with optional filter by contains","Active":true,"Notes":"Example custom Prepare that groups by the Tags - nothing shows if no tags\nRecommended to use datalist Filter \"Contains\" to first filter by the Tag(s) desired","Roles":124927,"AType":20,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t \n \t\t\n\n \n \n \n \n \t\n \n \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t \n \n \n \t\t\n \t\n \t\t\n \n \n {{#each ayReportData}}\n \n \n\t\t\t \n\t\t\t \n \n\t\t {{#each items}}\n\t\t \n\t\t\t \n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t \n \n\t\t\t{{/each}}\n\t\t\t\n\t\t\t \n \n \t {{/each}}\n \n
Parts by Tag
 
TagPartNameNameManufacturerVizCostRetail
 
{{group}}# of parts for {{group}}: {{count}}
 {{PartName}}{{Name}}{{ManufacturerViz}}{{ayCurrency Cost}}{{ayCurrency Retail}}
 
\n\n\n \n\t
\n\n","Style":".singlePage\r\n{\r\npage-break-after: always;\r\n}\r\n\r\ntable { \r\n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\r\n border-collapse: collapse;\r\n white-space: pre-wrap;\r\n width: 100%;\r\n table-layout: fixed;\r\n }\r\n\r\nthead {\r\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\r\n}\r\n\r\n.heading {\r\n border-style: solid;\r\n border-width: 1pt;\r\n border-color: #e8e5e5; \r\n margin: 10pt;\r\n background-color: #e8e5e5;\r\n padding: 5pt; \r\n font-size: 14pt; \r\n text-align: center;\r\n} \r\n\r\n.bodyhead {\r\n border-style: solid;\r\n border-width: 1pt;\r\n border-color: #e8e5e5; \r\n margin: 10pt;\r\n background-color: #e8e5e5;\r\n font-size: 12pt; \r\n}\r\n\r\n\r\n.rightlean {\r\n text-align: right;\r\n}\r\n.leftlean {\r\n text-align: left;\r\n}\r\n.centerlean {\r\n text-align: center;\r\n}\r\ntbody tr:nth-child(even) {\r\n font-size: 9pt;\r\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\r\n height: 50px;\r\n overflow-wrap: break-word;\r\n} \r\ntbody tr:nth-child(odd) {\r\n font-size: 9pt;\r\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\r\n height: 50px;\r\n overflow-wrap: break-word;\r\n} \r\n\r\n\r\ntfoot {\r\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\r\n page-break-inside: avoid;\r\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\r\n \r\n bottom: 0;\r\n width: 100%;\r\n border-top: 1px solid black; \r\n}\r\n\r\n\r\n.fontgreen {\r\n color: green;\r\n}\r\n.fontblue {\r\n color: blue;\r\n}\r\n.fontred {\r\n color:red;\r\n}\r\n\r\n","JsPrerender":"async function ayPrepareData(ayData) {\n\n\n //Group by all tags no filter\n ayData.ayReportData = ayGroupByTag(ayData.ayReportData);\n\n //Group by filtered tags that contain 'zone'\n //ayData.ayReportData = ayGroupByTag(ayData.ayReportData, 'zone');\n\n return ayData;\n}\n\nfunction ayGroupByTag(reportDataArray, tagContains) {\n //array to hold grouped data\n const ret = [];\n const containsQuery = tagContains != null && tagContains != '';\n\n //iterate through the raw reprot data \n for (let i = 0; i < reportDataArray.length; i++) {\n //get a reference to each object to save typing\n let o = reportDataArray[i];\n //don't bother with any that don't have tags at all\n if (o.Tags && o.Tags.length) {\n //loop through all tags for this record\n o.Tags.forEach(t => {//t=each tag\n //if not a contains query just process it, if it is a contains query then only process if tag contains tagContains\n if (!containsQuery || t.includes(tagContains)) {\n let groupObject = ret.find(z => z.group == t);\n if (groupObject != undefined) {\n //there is already a matching group in the return array so just push this raw report data record into it\n groupObject.items.push(o);\n //update the count for this group's items\n groupObject.count++;\n } else {\n //No group yet, so start a new one in the ret array and push this raw report data record\n ret.push({ group: t, items: [o], count: 1 });\n }\n }\n })\n }\n }\n\n //Sort based on the group name in a locale aware manner\n ret.sort(function (a, b) {\n return a.group.localeCompare(b.group);\n });\n return ret;\n}","JsHelpers":"","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":false,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file diff --git a/server/AyaNova/resource/rpt/stock-report-templates/Example Parts with available serial numbers.ayrt b/server/AyaNova/resource/rpt/stock-report-templates/Example Parts with available serial numbers.ayrt index 18ade006..eba06dc9 100644 --- a/server/AyaNova/resource/rpt/stock-report-templates/Example Parts with available serial numbers.ayrt +++ b/server/AyaNova/resource/rpt/stock-report-templates/Example Parts with available serial numbers.ayrt @@ -1 +1 @@ -{"Name":"Example Parts with available serial numbers","Active":true,"Notes":"Uses #if_eq custom Helper to ONLY display parts from the datalist that HAVE serial numbers \n","Roles":115050,"AType":20,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t \n \t\t\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \t\t\n \n \n {{#each ayReportData}}\n \n\n {{#if_eq PartSerialsViz \"\"}} \n {{else}} \n \n \n \n \n \n \n \n \n {{/if_eq}}\n \t {{/each}} \n \n\n
Parts with Serials Report
 
Part NumberPart NameManufacturerCostRetailSerials
 
{{PartNumber}}{{Name}}{{ManufacturerViz}}{{ayCurrency Cost}}{{ayCurrency Retail}}{{PartSerialsViz}}
\n\n \n\t
\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 12pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n overflow-wrap: break-word;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n overflow-wrap: break-word;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n return reportData;\n\n \n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})\n\n//custom helper so can do a direct comparison - i.e. if value equals xxxx, then show, else show yyyyy\n//note that this HAS to be added here in Helpers, is NOT built in\nHandlebars.registerHelper('if_eq', function(a, b, opts) {\n if(a == b) // Or === depending on your needs\n return opts.fn(this);\n else\n return opts.inverse(this);\n});","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file +{"Name":"Example Parts with available serial numbers","Active":true,"Notes":"Uses #if_eq custom Helper to ONLY display parts from the datalist that HAVE serial numbers \n","Roles":115050,"AType":20,"IncludeWoItemDescendants":false,"Template":"\n\n\t
\t \n\t\t \n \t\t\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \t\t\n \n \n {{#each ayReportData}}\n \n\n {{#if_eq PartSerialsViz \"\"}} \n {{else}} \n \n \n \n \n \n \n \n \n {{/if_eq}}\n \t {{/each}} \n \n\n
Parts with Serials Report
 
Part NumberPart NameManufacturerCostRetailSerials
 
{{PartName}}{{Name}}{{ManufacturerViz}}{{ayCurrency Cost}}{{ayCurrency Retail}}{{PartSerialsViz}}
\n\n \n\t
\n\n","Style":".singlePage\n{\npage-break-after: always;\n}\n\ntable { \n font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;\n border-collapse: collapse;\n white-space: pre-wrap;\n width: 100%;\n table-layout: fixed;\n }\n\nthead {\n display: table-header-group; /* so as to print the table-header on all subsequent pages */\n}\n\n.heading {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n padding: 5pt; \n font-size: 14pt; \n text-align: center;\n} \n\n.bodyhead {\n border-style: solid;\n border-width: 1pt;\n border-color: #e8e5e5; \n margin: 10pt;\n background-color: #e8e5e5;\n font-size: 12pt; \n}\n\n\n.rightlean {\n text-align: right;\n}\n.leftlean {\n text-align: left;\n}\n.centerlean {\n text-align: center;\n}\ntbody tr:nth-child(even) {\n font-size: 9pt;\n background-color: lightgray; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n overflow-wrap: break-word;\n} \ntbody tr:nth-child(odd) {\n font-size: 9pt;\n background-color: lightgoldenrodyellow; /* MUST checkmark Print background in PDF Options for this to show */\n height: 50px;\n overflow-wrap: break-word;\n} \n\n\ntfoot {\n /* display: table-footer-group; uncomment to print the table-footer on all pages, not just last page - */\n page-break-inside: avoid;\n /* position: fixed; uncommenting position: will force footer to bottom BUT then leftlean and rightlean don't work AND tbody overwrites */\n \n bottom: 0;\n width: 100%;\n border-top: 1px solid black; \n}\n\n\n.fontgreen {\n color: green;\n font-size: 16pt;\n}\n.fontblue {\n color: blue;\n}\n.fontred {\n color:red;\n}\n","JsPrerender":"async function ayPrepareData(reportData){ \n //this function (if present) is called with the report data \n //before the report is rendered\n //modify data as required here and return it to change the data before the report renders\n //see the help documentation for details\n return reportData;\n\n \n}","JsHelpers":"//Register custom Handlebars helpers here to use in your report script\n//https://handlebarsjs.com/guide/#custom-helpers\nHandlebars.registerHelper('loud', function (aString) {\n return aString.toUpperCase()\n})\n\n//custom helper so can do a direct comparison - i.e. if value equals xxxx, then show, else show yyyyy\n//note that this HAS to be added here in Helpers, is NOT built in\nHandlebars.registerHelper('if_eq', function(a, b, opts) {\n if(a == b) // Or === depending on your needs\n return opts.fn(this);\n else\n return opts.inverse(this);\n});","RenderType":0,"HeaderTemplate":"    (set in report template's PDF Options) Printed date: ","FooterTemplate":"  (set in report template's PDF Options showing x of ALL pages printed)  Page  of ","DisplayHeaderFooter":true,"PaperFormat":10,"Landscape":true,"MarginOptionsBottom":"10mm","MarginOptionsLeft":"10mm","MarginOptionsRight":"10mm","MarginOptionsTop":"10mm","PageRanges":null,"PreferCSSPageSize":false,"PrintBackground":true,"Scale":1.00000} \ No newline at end of file diff --git a/server/AyaNova/util/AySchema.cs b/server/AyaNova/util/AySchema.cs index f2a8c9e7..627801f2 100644 --- a/server/AyaNova/util/AySchema.cs +++ b/server/AyaNova/util/AySchema.cs @@ -396,7 +396,7 @@ $BODY$; when 17 then aytable = 'afileattachment'; aynamecolumn ='displayfilename'; when 18 then aytable = 'adatalistsavedfilter'; when 19 then aytable = 'aformcustom'; aynamecolumn = 'formkey'; - when 20 then aytable = 'apart'; aynamecolumn ='partnumber'; + when 20 then aytable = 'apart'; when 21 then aytable = 'apm'; aynamecolumn ='serial'; when 22 then aytkey= 'PMItem'; when 23 then aytkey= 'WorkOrderItemExpense'; @@ -511,7 +511,7 @@ BEGIN when 17 then aytable = 'afileattachment'; aynamecolumn ='displayfilename'; when 18 then aytable = 'adatalistsavedfilter'; when 19 then aytable = 'aformcustom'; aynamecolumn = 'formkey'; - when 20 then aytable = 'apart'; aynamecolumn ='partnumber'; + when 20 then aytable = 'apart'; when 21 then aytable = 'apm'; aynamecolumn ='serial'; when 22 then aytkey= 'PMItem'; when 23 then aytkey= 'WorkOrderItemExpense'; @@ -784,9 +784,9 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); await ExecQueryAsync("ALTER TABLE auser ADD FOREIGN KEY (vendorid) REFERENCES avendor(id)"); //PART - await ExecQueryAsync("CREATE TABLE apart (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT, active BOOL NOT NULL, " + await ExecQueryAsync("CREATE TABLE apart (id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, name TEXT NOT NULL UNIQUE, active BOOL NOT NULL, " + "notes TEXT, wiki TEXT, customfields TEXT, tags VARCHAR(255) ARRAY, " - + "partnumber TEXT NOT NULL UNIQUE, manufacturerid BIGINT REFERENCES avendor, manufacturernumber TEXT, " + + "description TEXT, manufacturerid BIGINT REFERENCES avendor, manufacturernumber TEXT, " + "wholesalerid BIGINT REFERENCES avendor, wholesalernumber TEXT, alternativewholesalerid BIGINT REFERENCES avendor, alternativewholesalernumber TEXT, " + "cost DECIMAL(38,18) NOT NULL, retail DECIMAL(38,18) NOT NULL, unitofmeasure TEXT, upc TEXT " + " )"); @@ -1038,7 +1038,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); + "FROM apurchaseorderitem WHERE (COALESCE(apurchaseorderitem.quantityordered,0)-COALESCE(apurchaseorderitem.quantityreceived,0)) > 0 GROUP BY partid, partwarehouseid"); //VIEWRESTOCKREQUIRED (used by PO indirectly through a keyless model in dbset) - await ExecQueryAsync("CREATE VIEW viewrestockrequired AS SELECT apart.id AS partid, apartwarehouse.id AS partwarehouseid, apart.partnumber, apartwarehouse.name AS displaywarehouse, " + await ExecQueryAsync("CREATE VIEW viewrestockrequired AS SELECT apart.id AS partid, apartwarehouse.id AS partwarehouseid, apart.name as partname, apart.description as partdescription, apartwarehouse.name AS displaywarehouse, " + "amanufacturer.id AS manufacturerid, amanufacturer.name AS displaymanufacturer, awholesaler.id AS wholesalerid, awholesaler.name AS displaywholesaler, " + "aalternativewholesaler.id AS alternativewholesalerid, aalternativewholesaler.name AS displayalternativewholesaler," + "apartstocklevel.minimumquantity, vpartinventorynow.balance, COALESCE(vpartsonorderuncommitted.quantityonorder,0) AS onorderquantity, " @@ -1054,7 +1054,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); //VIEWPARTINVENTORYLIST await ExecQueryAsync( - "CREATE VIEW viewpartinventorylist AS select apart.id as partid, apart.partnumber, apart.name as partname, apart.active as partactive, apart.cost as partcost, apart.retail as partretail," + "CREATE VIEW viewpartinventorylist AS select apart.id as partid, apart.name as partname, apart.description as partdescription, apart.active as partactive, apart.cost as partcost, apart.retail as partretail," + "apart.tags as parttags, apartwarehouse.id as partwarehouseid, apartwarehouse.name as partwarehousename, awholesaler.name as wholesalername, awholesaler.id as wholesalerid, " + "aaltwholesaler.id as altwholesalerid, aaltwholesaler.name as altwholesalername, vpartinventorynow.balance as onhandqty,COALESCE(vpartsonorder.quantityonorder,0) as onorderqty, " + "COALESCE(vpartsonordercommitted.quantityonordercommitted,0) as onordercommittedqty,COALESCE(apartstocklevel.minimumquantity,0) as restockminqty, " @@ -1070,7 +1070,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); //VIEWUNFULFILLEDPARTREQUESTLIST await ExecQueryAsync("CREATE VIEW viewunfulfilledpartrequestlist AS SELECT AWORKORDERITEMPARTREQUEST.ID AS REQUESTID, PARTID, PARTWAREHOUSEID, QUANTITY, AWORKORDER.SERIAL, " - + "APART.PARTNUMBER AS PARTNUMBER, APART.NAME AS PARTNAME, APARTWAREHOUSE.NAME AS PARTWAREHOUSENAME, AWHOLESALER.NAME AS WHOLESALERNAME, " + + "APART.DESCRIPTION AS PARTDESCRIPTION, APART.NAME AS PARTNAME, APARTWAREHOUSE.NAME AS PARTWAREHOUSENAME, AWHOLESALER.NAME AS WHOLESALERNAME, " + "AWORKORDERITEMPARTREQUEST.REQUESTEDBYUSERID AS REQUESTEDBYUSERID, AUSER.NAME AS REQUESTEDBYUSERNAME, " + "AWHOLESALER.ID AS WHOLESALERID, AALTWHOLESALER.ID AS ALTWHOLESALERID, AALTWHOLESALER.NAME AS ALTWHOLESALERNAME FROM AWORKORDERITEMPARTREQUEST " + "LEFT JOIN AWORKORDERITEM ON AWORKORDERITEM.ID = AWORKORDERITEMPARTREQUEST.WORKORDERITEMID LEFT JOIN AWORKORDER ON AWORKORDERITEM.WORKORDERID = AWORKORDER.ID " @@ -1082,7 +1082,7 @@ $BODY$ LANGUAGE PLPGSQL STABLE"); //VIEWPARTINVENTORYREQUESTLIST (note: twinned with pareventoryrequestdatalist, this drives reporting for that datalist which is not reportable directly) await ExecQueryAsync("CREATE VIEW VIEWPARTINVENTORYREQUESTLIST AS SELECT AWORKORDERITEMPARTREQUEST.ID AS REQUESTID, APART.ID AS PARTID, APARTWAREHOUSE.ID AS PARTWAREHOUSEID, QUANTITY, " - + "APART.PARTNUMBER AS PARTNUMBER, APART.NAME AS PARTNAME, APARTWAREHOUSE.NAME AS PARTWAREHOUSENAME, AWHOLESALER.NAME AS WHOLESALERNAME, " + + "APART.DESCRIPTION AS PARTDESCRIPTION, APART.NAME AS PARTNAME, APARTWAREHOUSE.NAME AS PARTWAREHOUSENAME, AWHOLESALER.NAME AS WHOLESALERNAME, " + "AWORKORDER.SERIAL AS WOSERIAL, AWORKORDERITEMPARTREQUEST.REQUESTEDBYUSERID AS REQUESTEDBYUSERID, AUSER.NAME AS REQUESTEDBYUSERNAME, " + "AWHOLESALER.ID AS WHOLESALERID, AALTWHOLESALER.ID AS ALTWHOLESALERID, AALTWHOLESALER.NAME AS ALTWHOLESALERNAME, " + "APARTMANU.NAME AS MANUFACTURERNAME, APARTMANU.ID AS MANUFACTURERID, APART.UPC AS PARTUPC, APURCHASEORDER.ID AS PURCHASEORDERID, APURCHASEORDER.SERIAL AS PURCHASEORDERSERIAL, " diff --git a/server/AyaNova/util/Seeder.cs b/server/AyaNova/util/Seeder.cs index 1be1892e..99c26bf8 100644 --- a/server/AyaNova/util/Seeder.cs +++ b/server/AyaNova/util/Seeder.cs @@ -2529,7 +2529,7 @@ namespace AyaNova.Util - public HashSet HashPartNumbers = new HashSet(); + public HashSet HashPartNames = new HashSet(); private int TotalSeededParts = 0; ////////////////////////////////////////////////////// //PART @@ -2546,22 +2546,22 @@ namespace AyaNova.Util Part o = new Part(); do { - o.PartNumber = Fake.Finance.Account(6); - } while (!HashPartNumbers.Add(o.PartNumber)); + o.Name = Fake.Finance.Account(6); + } while (!HashPartNames.Add(o.Name)); - o.Name = "pnm" + o.PartNumber;//temporary?? get rid of name?? + o.Active = true; o.Notes = Fake.Lorem.Sentence(null, 3); o.Tags = RandomTags(); o.ManufacturerId = Fake.Random.Long(1, 3);//There are minimum 10 vendors seeded, want parts all in the first few so that some po stuff can kick in (vendorpartnumber etc) - o.ManufacturerNumber = "man-" + o.PartNumber; + o.ManufacturerNumber = "man-" + o.Name; o.UPC = Fake.Commerce.Ean13(); o.WholeSalerId = Fake.Random.Long(4, 6); - o.WholeSalerNumber = "ws-" + o.PartNumber; + o.WholeSalerNumber = "ws-" + o.Name; o.AlternativeWholeSalerId = Fake.Random.Long(7, 9); - o.AlternativeWholeSalerNumber = "aws-" + o.PartNumber; + o.AlternativeWholeSalerNumber = "aws-" + o.Name; o.Cost = Fake.Random.Decimal(1, 25); o.Retail = o.Cost * 1.2m; @@ -2577,7 +2577,7 @@ namespace AyaNova.Util TotalSeededParts++; if (NewObject == null) { - var err = $"Seeder::SeedPart error creating {o.PartNumber}\r\n{biz.GetErrorsAsString()}"; + var err = $"Seeder::SeedPart error creating {o.Name}\r\n{biz.GetErrorsAsString()}"; log.LogError(err); throw new System.Exception(err); } @@ -2621,7 +2621,7 @@ namespace AyaNova.Util } if (partInventory == null) { - var err = $"Seeder::SeedPart - error creating {o.PartNumber} INVENTORY \r\n{PartInventoryBizNess.GetErrorsAsString()}"; + var err = $"Seeder::SeedPart - error creating {o.Name} INVENTORY \r\n{PartInventoryBizNess.GetErrorsAsString()}"; log.LogError(err); throw new System.Exception(err); }