Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inter store requisitions current #456

Merged
merged 67 commits into from
Aug 17, 2017
Merged

Conversation

andreievg
Copy link
Contributor

No description provided.

andreievg and others added 29 commits August 1, 2017 17:01
Should be done via setting !! Will amend
# Conflicts:
#	src/database/UIDatabase.js
#	src/localization/modalStrings.js
# Conflicts:
#	src/dataMigration.js
#	src/pages/RequisitionsPage.js
#	src/sync/SyncQueue.js
#	src/sync/Synchroniser.js
#	src/sync/syncTranslators.js
package.json Outdated
@@ -4,7 +4,7 @@
"email": "info@sussol.net"
},
"name": "mSupplyMobile",
"version": "1.1.6",
"version": "2.0.0",
"private": true,
"license": "proprietary",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose private should be false, and license should be MIT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

requisitions.forEach(requisition => {
if (!requisition.isRequest) return;
// The reason for database.update is we don't want to update finalized transactions
// and incase database.save is replaced by automatic way of invoking db event listeners
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case


database.write(() => {
requisitions.forEach(requisition => {
if (!requisition.isRequest) return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that it is a new getter on Requisition objects, does myRequisition.isRequest work before the migration?

As there are 4 types of Requisition it'd be a bit safer to make a getter isResponse and make that the condition for a block surrounding this code, so it isn't ever run for the other types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am pretty sure realm doesn't store getters + functions from datatype in app data. It will construct Requisition data type as it load realm records. So yeah it will have the getter without migration.

In V3 mSupply api four types of requisition, finalised(fn) and suggested(sg) for both isReponse and isRequest, so either isResponse or isRequest have same 'safeness' level

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm happy with first answer, but second answer is a bit hazy. They types are // imprest, forecast, request or response, that doesn't have anything to do with statuses. If a store somehow has a requisition of type 'imprest' or 'forecast', it'll run this migration code on it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be fine to add a otherStoreName as mainSupplyingStoreName for any requisition prior to v3. As they will only be requisitions for main supplying store

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify the first point in this thread, no worries about myRequisition.isRequest working, because this migration code runs after the realm database is all reinstated and its own internal data migration code has already run. So at this point, all of the requisitions have already been given type: 'request' through the default

if (!requisition.isRequest) return;
// The reason for database.update is we don't want to update finalized transactions
// and incase database.save is replaced by automatic way of invoking db event listeners
// simply assigning property may cause changes to finalized transaction to be synced.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not 100% sure why you'd not want to sync the change. If you ever had to reinitialise the store (e.g. replaced tablet), you'd want the records that you pull to have the updated values for otherStoreName.

Also: finalised

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not re sync finalised requisitions as it will cause v2, v3 to replicate requisition. And as for otherStoreName (i.e name_ID), mSupply has migration code that already assigns correct name_ID.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add note of why we don't want them being synced, as you just described.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// The reason for database.update is we don't want to update finalized transactions
// and incase database.save is replaced by automatic way of invoking db event listeners
// simply assigning property may cause changes to finalized transaction to be synced.

That should be descriptive enough

Copy link
Collaborator

@edmofro edmofro Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's going on here?

Should not re sync finalised requisitions as it will cause v2, v3 to replicate requisition.

Surely not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should adjust my comment and say, possible will cause v2, v3 to replicate requisition. But I do think that finalised requisition should not be updated

@@ -47,6 +51,10 @@ export class Requisition extends Realm.Object {
this.daysToSupply = months * 30;
}

get isLinkedToRequisition() {
return typeof this.linkedRequisition === 'object';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know we've used it elsewhere, but typeof is considered dangerous in the JS community because it returns 'object' for multiple counterintuitive things. Including, tragically enough, null.

return !!this.linkedRequisition should behave correctly in return turning True for not null. It can only be a Transaction as defined in the schema, or null/undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I am pretty sure realm will store 'undefined' in object key that has no value. I will change this check to keep with consistency.

sortable: true,
},
{
key: 'ourStockOnHand', // TODO: should this column name be consistent with other pages
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it? Unless we have excess column width then it'll be good to keep this as is, I wouldn't bother changing the other pages to conform.

alignText: 'right',
},
{
key: 'stockOnHand',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

theirStockOnHand

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DataType: RequisitionItem.stockOnHand.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was about to make the same comment as Chris, but valid response

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe?

get therStockOnHand() {
  // check requisition is response 
  // return 0 or even throw and error
}

This also bugged me, stockOnHand not very representative

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it is a good practice to throw errors rather than return null with such checks else where. Would make bugsnag logs be more informative!

Might be able to have a universal error modal that handles it all at the root of the app, too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As for your idea with the get, that along with a get ourStockOnHand could be a nice tweak and minor refactor. I'm happy not bothering though given current time constraints.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am glad you mentioned this actually. Request requisition.stockOnHand should be 'dynamic' until finalised and update (alongside suggestedQuantity), not set in createRecord. I have other thought on this subject, will try to update by mid Tomorrow otherwise will make an issue.

@@ -17,6 +17,9 @@ import { RequisitionsPage } from './RequisitionsPage';
import { RequisitionPage,
checkForFinaliseError as checkForRequisitionFinaliseError,
} from './RequisitionPage';
import { SupplyRequisitionPage,
checkForFinaliseError as checkForSupplierRequisitionError
} from './SupplyRequisitionPage'; // TODO finalise checking
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed TODO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any reason to do before finalise checking for SupplyRequisition.

@@ -274,8 +277,9 @@ export function createOrUpdateRecord(database, settings, recordType, record) {
serialNumber: record.serial_number,
requesterReference: record.requester_reference,
comment: record.comment,
enteredBy: getObject(database, 'User', record.user_ID),
enteredBy: getObject(database, 'User', record.user_ID), // TODO: should be..
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be what?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't sure if it there is a need to change this, look like there is no need.

@@ -118,10 +116,10 @@ function generateSyncData(settings, recordType, record) {
ID: record.id,
date_entered: getDateString(record.entryDate),
user_ID: record.enteredById,
name_ID: settings.get(THIS_STORE_NAME_ID),
name_ID: record.otherStoreName.id, // TODO: check if there is a case when outgoing req might not have otherStoreName
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was checked?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't think of a case, but will leave this to adjust last (in case there is a case)

const mainSupplyingStoreName = nameResults[0];

const requisitions = database.objects('Requisition')
.filtered('otherStoreName = Null').snapshot();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really capital Null?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also you should filter here on type = 'request' rather than doing the early return check below

Copy link
Contributor Author

@andreievg andreievg Aug 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, nice catch, updated.
As per capital Null, I didn't want to go deep into research, just found the following (since latest realm js docs don't have filtered by undefined/null)
realm/realm-js#162 , then pr file changes https://github.com/realm/realm-js/pull/386/files there an enum .. I didn't know if case sensitive
enum class Type { None, Number, String, KeyPath, Argument, True, False, Null } type; , will check with = null and == null tomorrow


database.write(() => {
requisitions.forEach(requisition => {
if (!requisition.isRequest) return;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify the first point in this thread, no worries about myRequisition.isRequest working, because this migration code runs after the realm database is all reinstated and its own internal data migration code has already run. So at this point, all of the requisitions have already been given type: 'request' through the default

database.update('Requisition', {
id: requisition.id,
otherStoreName: mainSupplyingStoreName,
}, 'sync');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the use of 'sync' here manually. This migration code uses the UIDatabase, which means it isn't causedBy sync, and this is just a hack to stop it from syncing back to the server, which actually seems entirely unnecessary. Yes it might save a bit of bandwidth, but from a logic perspective there is no reason not to sync back to server. But bandwidth is probably not significant...can't see any site having made more than 100 request type requisitions yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure I see now, database in this context is UIDatabase, thanks. Updated..
I thought finalised was finalised, and most requisitions at migration stage will be finalised, so was meaning to avoid modifying finalised transaction.. If the server api assumed finalised transaction to be 'final', it could have a check on update record, if finalised ignore and log. I think we should assume that finalised transaction/requisition's cannot be edited after v3.

@@ -61,6 +65,26 @@ export class Requisition extends Realm.Object {
this.addItem(requisitionItem);
}

createCustomerInvoice(database, user) {
if (this.isRequest || this.isFinalised) {
throw new Error('Cannot create invoice from Finalised or Request Requistion ');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

throw new Error('Cannot create invoice from Finalised or Request Requistion ');
}
if (database.objects('Transaction').
filtered('linkedRequisition.id = $0', this.id).length > 0) return;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tempted to throw an error here...shouldn't call createCustomerInvoice if there's already one created

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for sync, in case of resync and CustomerInvoice arrived before Requisition. And if CustomerInvoice arrived after Requisition.. this is why this should only be done in postSyncProcess

transactionItem.availableQuantity);
requisitionItem.suppliedQuantity = transactionItem.totalQuantity;
database.save('TransactionItem', transactionItem);
database.save('RequisitionItem', requisitionItem);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should only need the save for requisition item

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but suppliedQuantity has changed.. suppliedQuantity -> record.actualQuan, and should be synced

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically the rule is:
At the point you use dot notation to change a data object, you must call .save
If you just call a method that manipulates the data object, you don't call .save. You can feel save in the knowledge that if the method uses dot notation to change it, it is the methods responsibility to call .save - so long as writer of the method followed the rules, you are fine (perhaps a good idea to check inside the method if you want to be really sure)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per comment above where setTotalQuantity is used in CustomerInvoicePage -> before I refactored.

My original comment wasn't very good ( I misread your comment requisition item -> transaction item ). And yes, will leave 'sync' out of save and update talk and use them all the time dot notation assignment is used.

columns={infoColumns}
isEditingDisabled={requisition.isFinalised}
/>
);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So much of this class is shared by the regular requisition page

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh refactor septemeber/october

}
}

renderButtons() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to reflect the single button that is being rendered

alignText: 'right',
},
{
key: 'stockOnHand',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was about to make the same comment as Chris, but valid response


export function checkForFinaliseError() {
return null;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...this seems unnecessary. Or are you going to update with an actual check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's gone.. will check

@@ -31,7 +31,8 @@ export class RequisitionsPage extends React.Component {
isRequestView: true,
isCreatingRequisition: false,
};
this.requisitions = props.database.objects('Requisition');
this.requisitions = props.database.objects('Requisition')
.filtered('serialNumber != "-1"');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@edmofro If you're wondering why we're back to doing this in the Requisition page:

SyncPostProcessor uses the UIDatabase, so when trying to find records with serialNumber '-1' it'd never find any because they were already filtered out.

I suppose a different solution would be make another switch option in UIDatabase for 'RawRequisitions' or something that doesn't do any filtering.

Or another function objectsDirect(type) that just passes straight to the realm.objects function without any filters.

Copy link
Collaborator

@edmofro edmofro Aug 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I worked that out after a bit...sorry for sending you astray. I don't think you should at this stage, but if you were going to add to the UIDatabase, I'd go in the other direction...keep Requisition as the 'raw' type, and add a new type for the filtered requisitions.

P.S. listening carefully to this code, you might hear a hint that the postSyncProcessor shouldn't be using the UIDatabase ;-)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that would be another quick fix (Your P.S.)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick and correct IMO. Code smells include:

  • A bug happened (start sniffing...)
  • The 'post sync' processor, which has nothing to do with UI, is using something called a 'UIDatabase' (there it is!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I agree, but, this doesn't help in deciphering differences in db wrappers:

    const database = new Database(schema);
    this.database = new UIDatabase(database);
    this.settings = new MobileAppSettings(this.database);
    migrateDataToVersion(this.database, this.settings);
    this.userAuthenticator = new UserAuthenticator(this.database, this.settings);
    const syncAuthenticator = new SyncAuthenticator(this.settings);
    this.synchroniser = new Synchroniser(database, syncAuthenticator, this.settings);
    this.postSyncProcessor = new PostSyncProcessor(this.database, this.settings);

A bit too messy in my opinion, this.database, database

@edmofro edmofro merged commit 34a449a into master Aug 17, 2017
@andreievg andreievg deleted the inter-store-requisitions-current branch September 8, 2017 03:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants