Skip to content

Commit a29f91f

Browse files
Associate rate types with payment methods (#1162)
* Associate rate types with payment methods * Fix lookup metadata * Improve tooltips on calendar view * Also include person who created the reservation
1 parent 42409dd commit a29f91f

File tree

10 files changed

+108
-52
lines changed

10 files changed

+108
-52
lines changed

resources/queries/targetedms/InstrumentBilling.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@ SELECT
3232
((TIMESTAMPDIFF('SQL_TSI_HOUR', StartTime, EndTime) * Fee + ir.rateType.setupFee) * PercentPayment / 100) AS AmountBilled
3333

3434
FROM targetedms.InstrumentSchedule i
35-
INNER JOIN targetedms.InstrumentRate ir ON i.Instrument = ir.Instrument
36-
INNER JOIN targetedms.InstrumentUsagePayment iup ON i.Id = iup.InstrumentScheduleId
35+
INNER JOIN targetedms.InstrumentUsagePayment iup ON i.Id = iup.InstrumentScheduleId
36+
INNER JOIN targetedms.InstrumentRate ir ON i.Instrument = ir.Instrument AND iup.PaymentMethod.RateType = ir.rateType
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-- Add FKs to Containers and delete orphaned rows
2+
DELETE FROM targetedms.instrumentUsagePayment WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
3+
DELETE FROM targetedms.instrumentSchedule WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
4+
DELETE FROM targetedms.projectPaymentMethod WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
5+
DELETE FROM targetedms.projectResearcher WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
6+
DELETE FROM targetedms.msProject WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
7+
DELETE FROM targetedms.instrumentRate WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
8+
DELETE FROM targetedms.msInstrument WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
9+
DELETE FROM targetedms.paymentMethod WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
10+
DELETE FROM targetedms.rateType WHERE Container NOT IN (SELECT EntityId FROM core.Containers);
11+
12+
ALTER TABLE targetedms.instrumentUsagePayment ADD CONSTRAINT FK_InstrumentUsagePayment_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
13+
ALTER TABLE targetedms.msProject ADD CONSTRAINT FK_MSProject_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
14+
ALTER TABLE targetedms.projectResearcher ADD CONSTRAINT FK_ProjectResearcher_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
15+
ALTER TABLE targetedms.instrumentRate ADD CONSTRAINT FK_InstrumentRate_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
16+
ALTER TABLE targetedms.msInstrument ADD CONSTRAINT FK_MSInstrument_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
17+
ALTER TABLE targetedms.paymentMethod ADD CONSTRAINT FK_PaymentMethod_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
18+
ALTER TABLE targetedms.projectPaymentMethod ADD CONSTRAINT FK_ProjectPaymentMethod_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
19+
ALTER TABLE targetedms.instrumentSchedule ADD CONSTRAINT FK_InstrumentSchedule_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
20+
ALTER TABLE targetedms.rateType ADD CONSTRAINT FK_RateType_Container FOREIGN KEY (Container) REFERENCES core.Containers(EntityId);
21+
22+
-- Add a RateType column to paymentMethod, set its values (creating one if needed), and make it not null
23+
ALTER TABLE targetedms.paymentMethod ADD COLUMN RateType INT;
24+
ALTER TABLE targetedms.paymentMethod ADD CONSTRAINT FK_PaymentMethod_RateType FOREIGN KEY (RateType) REFERENCES targetedms.RateType(Id);
25+
26+
UPDATE targetedms.paymentMethod pm SET RateType = (SELECT MIN(Id) FROM targetedms.RateType rt WHERE rt.Container = pm.Container);
27+
28+
INSERT INTO targetedms.RateType (Name, Container)
29+
SELECT DISTINCT 'Default', Container FROM targetedms.paymentMethod WHERE RateType IS NULL;
30+
31+
UPDATE targetedms.paymentMethod pm SET RateType = (SELECT MIN(Id) FROM targetedms.RateType rt WHERE rt.Container = pm.Container) WHERE RateType IS NULL;
32+
33+
ALTER TABLE targetedms.paymentMethod ALTER COLUMN RateType SET NOT NULL;

resources/schemas/targetedms.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,7 @@
18231823
<inputType>text</inputType>
18241824
</column>
18251825
<column columnName="UWBudgetNumber"/>
1826+
<column columnName="RateType"/>
18261827
<column columnName="budgetExpirationDate"/>
18271828
<column columnName="PONumber"/>
18281829
<column columnName="contactNameFirst"/>

resources/views/scheduleAllInstruments.html

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,33 @@
5858
},
5959
eventMouseEnter: function(mouseEnterInfo) {
6060
const e = mouseEnterInfo.event;
61-
const dateStr = DateFormat.format.date(e.start, LABKEY.container.formats.dateTimeFormat) + ' - ' + DateFormat.format.date(e.end, LABKEY.container.formats.dateTimeFormat);
6261
let content = '';
6362
content += '<div class="event-tooltip-content">';
64-
if (e.extendedProps && e.extendedProps.instrumentName) {
65-
content += '<div class="event-id">Instrument: ' + LABKEY.Utils.encodeHtml(e.extendedProps.instrumentName) + '</div>';
66-
}
67-
if (e.extendedProps && e.extendedProps.projectTitle) {
68-
content += '<div class="event-id">Project: ' + LABKEY.Utils.encodeHtml(e.extendedProps.projectTitle) + '</div>';
63+
if (e.extendedProps) {
64+
if (e.extendedProps.instrumentName) {
65+
content += '<div class="event-id">Instrument: ' + LABKEY.Utils.encodeHtml(e.extendedProps.instrumentName) + '</div>';
66+
}
67+
if (e.extendedProps.projectTitle) {
68+
content += '<div class="event-id">Project: ' + LABKEY.Utils.encodeHtml(e.extendedProps.projectId) + '</div>';
69+
}
70+
if (e.extendedProps.projectId) {
71+
content += '<div class="event-id">Project Id: ' + LABKEY.Utils.encodeHtml(e.extendedProps.projectTitle) + '</div>';
72+
}
73+
if (e.extendedProps.reservedBy) {
74+
content += '<div class="event-id">Reserved by: ' + LABKEY.Utils.encodeHtml(e.extendedProps.reservedBy) + '</div>';
75+
}
76+
if (e.extendedProps.name) {
77+
content += '<div class="event-id">Name: ' + LABKEY.Utils.encodeHtml(e.extendedProps.name) + '</div>';
78+
}
79+
if (e.extendedProps.notes) {
80+
content += '<div class="event-id">Notes: ' + LABKEY.Utils.encodeHtml(e.extendedProps.notes) + '</div>';
81+
}
6982
}
70-
content += '<div class="event-date">' + LABKEY.Utils.encodeHtml(dateStr) + '</div>';
7183
content += '</div>';
7284

7385
$(mouseEnterInfo.el).popover({
7486
trigger: 'manual',
87+
placement: 'auto top',
7588
container: 'body',
7689
html: true,
7790
content: content
@@ -183,7 +196,7 @@
183196
LABKEY.Query.selectRows({
184197
schemaName: 'targetedms',
185198
queryName: 'instrumentSchedule',
186-
columns: 'Id,startTime,endTime,name,notes,instrument/Id,instrument/name,instrument/color,project/Id,project/title',
199+
columns: 'Id,startTime,endTime,name,notes,instrument/Id,instrument/name,instrument/color,project/Id,project/title,CreatedBy/DisplayName',
187200
success: function (data) {
188201
const schedRows = data.rows || [];
189202
for (let i = 0; i < schedRows.length; i++) {
@@ -202,6 +215,9 @@
202215
extendedProps: {
203216
baseColor: color,
204217
instrumentId: instId,
218+
name: r['name'],
219+
notes: r['notes'],
220+
reservedBy: r['CreatedBy/DisplayName'],
205221
instrumentName: r['instrument/name'] || info.name,
206222
projectId: r['project/Id'] || null,
207223
projectTitle: r['project/title'] || ''

src/org/labkey/targetedms/TargetedMSListener.java

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,19 @@
1515
*/
1616
package org.labkey.targetedms;
1717

18-
import org.jetbrains.annotations.NotNull;
1918
import org.labkey.api.data.Container;
2019
import org.labkey.api.data.ContainerManager;
2120
import org.labkey.api.data.SqlExecutor;
2221
import org.labkey.api.security.User;
2322
import org.labkey.targetedms.parser.speclib.LibSpectrumReader;
2423

25-
import java.beans.PropertyChangeEvent;
26-
import java.util.Collection;
27-
import java.util.Collections;
28-
2924
/**
3025
* User: vsharma
3126
* Date: 8/22/2014
3227
* Time: 3:22 PM
3328
*/
3429
public class TargetedMSListener implements ContainerManager.ContainerListener
3530
{
36-
@Override
37-
public void containerCreated(Container c, User user)
38-
{
39-
}
40-
4131
@Override
4232
public void containerDeleted(Container c, User user)
4333
{
@@ -71,22 +61,15 @@ public void containerDeleted(Container c, User user)
7161

7262
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoQCEmailNotifications() + " WHERE Container = ?", c);
7363
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoInstrumentNickname() + " WHERE Container = ?", c);
74-
}
75-
76-
@Override
77-
public void containerMoved(Container c, Container oldParent, User user)
78-
{
79-
}
80-
81-
@NotNull
82-
@Override
83-
public Collection<String> canMove(Container c, Container newParent, User user)
84-
{
85-
return Collections.emptyList();
86-
}
8764

88-
@Override
89-
public void propertyChange(PropertyChangeEvent evt)
90-
{
65+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoInstrumentUsagePayment() + " WHERE Container = ?", c);
66+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoInstrumentSchedule() + " WHERE Container = ?", c);
67+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoProjectPaymentMethod() + " WHERE Container = ?", c);
68+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoProjectResearcher() + " WHERE Container = ?", c);
69+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoMSProject() + " WHERE Container = ?", c);
70+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoInstrumentRate() + " WHERE Container = ?", c);
71+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoMSInstrument() + " WHERE Container = ?", c);
72+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoPaymentMethod() + " WHERE Container = ?", c);
73+
new SqlExecutor(TargetedMSManager.getSchema()).execute("DELETE FROM " + TargetedMSManager.getTableInfoRateType() + " WHERE Container = ?", c);
9174
}
9275
}

src/org/labkey/targetedms/TargetedMSManager.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,16 @@ public static SampleFileChromInfo getSampleFileChromInfo(int id, Container c)
214214
return new TableSelector(getTableInfoSampleFileChromInfo(), new SimpleFilter(FieldKey.fromParts("Id"), id).addCondition(FieldKey.fromParts("Container"), c), null).getObject(SampleFileChromInfo.class);
215215
}
216216

217+
public static TableInfo getTableInfoRateType()
218+
{
219+
return getSchema().getTable(TargetedMSSchema.TABLE_RATE_TYPE);
220+
}
221+
222+
public static TableInfo getTableInfoInstrumentRate()
223+
{
224+
return getSchema().getTable(TargetedMSSchema.TABLE_INSTRUMENT_RATE);
225+
}
226+
217227
public String getSchemaName()
218228
{
219229
return TargetedMSSchema.SCHEMA_NAME;

src/org/labkey/targetedms/TargetedMSModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ public String getName()
231231
@Override
232232
public Double getSchemaVersion()
233233
{
234-
return 26.000;
234+
return 26.001;
235235
}
236236

237237
@Override

src/org/labkey/targetedms/query/SimpleTargetedMSTable.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public SimpleTargetedMSTable(String name, TargetedMSSchema schema, ContainerFilt
1515
{
1616
super(schema, TargetedMSSchema.getSchema().getTable(name), cf);
1717
wrapAllColumns(true);
18+
TargetedMSTable.fixupLookups(this);
1819
}
1920

2021
@Override

test/src/org/labkey/test/tests/targetedms/InstrumentSchedulingTest.java

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,15 @@ private void doInit() throws IOException, CommandException
111111
InsertRowsCommand rateTypeInsert = new InsertRowsCommand("targetedms", "rateType");
112112
rateTypeInsert.setRows(Arrays.asList(
113113
Map.of("Name", "DefaultRate", "SetupFee", 50),
114-
Map.of("Name", "BigSpenderRate", "SetupFee", 50)
114+
Map.of("Name", "BigSpenderRate", "SetupFee", 66)
115115
));
116116
List<Map<String, Object>> rateTypes = rateTypeInsert.execute(createDefaultConnection(), getProjectName()).getRows();
117117

118118
InsertRowsCommand paymentMethodInsert = new InsertRowsCommand("targetedms", "paymentMethod");
119119
paymentMethodInsert.setRows(Arrays.asList(
120-
Map.of("UWBudgetNumber", "1111", "Name", PAYMENT_METHOD_1),
121-
Map.of("UWBudgetNumber", "2222", "Name", PAYMENT_METHOD_2),
122-
Map.of("UWBudgetNumber", "3333", "Name", PAYMENT_METHOD_3) // Intentionally not associated with a project
120+
Map.of("UWBudgetNumber", "1111", "Name", PAYMENT_METHOD_1, "RateType", rateTypes.get(0).get("Id")),
121+
Map.of("UWBudgetNumber", "2222", "Name", PAYMENT_METHOD_2, "RateType", rateTypes.get(1).get("Id")),
122+
Map.of("UWBudgetNumber", "3333", "Name", PAYMENT_METHOD_3, "RateType", rateTypes.get(0).get("Id")) // Intentionally not associated with a project
123123
));
124124
List<Map<String, Object>> paymentMethods = paymentMethodInsert.execute(createDefaultConnection(), getProjectName()).getRows();
125125

@@ -142,7 +142,9 @@ private void doInit() throws IOException, CommandException
142142
InsertRowsCommand instrumentRateInsert = new InsertRowsCommand("targetedms", "instrumentRate");
143143
instrumentRateInsert.setRows(Arrays.asList(
144144
Map.of("Instrument", instruments.get(0).get("Id"), "rateType", rateTypes.get(0).get("Id"), "fee", 100),
145-
Map.of("Instrument", instruments.get(1).get("Id"), "rateType", rateTypes.get(1).get("Id"), "fee", 110)
145+
Map.of("Instrument", instruments.get(0).get("Id"), "rateType", rateTypes.get(1).get("Id"), "fee", 211),
146+
Map.of("Instrument", instruments.get(1).get("Id"), "rateType", rateTypes.get(0).get("Id"), "fee", 100),
147+
Map.of("Instrument", instruments.get(1).get("Id"), "rateType", rateTypes.get(1).get("Id"), "fee", 330)
146148
));
147149
List<Map<String, Object>> instrumentRates = instrumentRateInsert.execute(createDefaultConnection(), getProjectName()).getRows();
148150
}
@@ -301,11 +303,12 @@ public void testSchedule() throws IOException, CommandException
301303
waitAndClickAndWait(Locator.linkWithText("Instrument billing report"));
302304
assertTextPresent("$950.00", 8);
303305
// Two rows, one for each of the two payment methods
304-
assertTextPresent("$3,680.00", 2);
306+
assertTextPresent("$3,350.00", 1);
307+
assertTextPresent("$10,956.00", 1);
305308
assertTextPresent(PAYMENT_METHOD_1, 5);
306309
assertTextPresent(PAYMENT_METHOD_2, 1);
307-
// Verify the 40/60 split
308-
assertTextPresent("$1,472.00", "$2,208.00");
310+
// Verify the 40/60 split (though odd since they're different rate types)
311+
assertTextPresent("$1,340.00", "$6,573.60");
309312

310313
goToDashboard();
311314
clickAndWait(Locator.linkWithText("Monthly instrument billing report"));
@@ -314,7 +317,7 @@ public void testSchedule() throws IOException, CommandException
314317
clickButton("Submit");
315318
// Only some hours should be in the range for this billing report
316319
assertTextPresent("17.0", 2);
317-
assertTextPresent("$748.00", "$1,122.00");
320+
assertTextPresent("$680.00", "$3,366.00");
318321
}
319322

320323
private void attemptScheduleInsertExpectingFailure(Map<String, Object> row, String expected) throws IOException

webapp/TargetedMS/js/scheduler.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,26 @@ $(function() {
101101
let content = '';
102102
let dateStr = DateFormat.format.date(mouseEnterInfo.event.start, LABKEY.container.formats.dateTimeFormat) + ' - ' + DateFormat.format.date(mouseEnterInfo.event.end, LABKEY.container.formats.dateTimeFormat);
103103
content += '<div class="event-tooltip-content">'
104-
+ '<div class="event-id" >' + 'Project Id : &nbsp' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.extendedProps.project) + '</div>'
105-
+ '<div class="event-title">' + 'Title : &nbsp' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.title) + '</div>'
106-
+ '<div class="event-date">' + LABKEY.Utils.encodeHtml(dateStr) + '</div>'
107-
+ '</div>';
104+
+ '<div class="event-title">' + 'Project: ' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.title) + '</div>'
105+
+ '<div class="event-id">' + 'Project Id: ' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.extendedProps.project) + '</div>';
106+
if (mouseEnterInfo.event.extendedProps.reservedBy) {
107+
content += '<div>' + 'Reserved by: ' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.extendedProps.reservedBy) + '</div>'
108+
}
109+
if (mouseEnterInfo.event.extendedProps.name) {
110+
content += '<div>' + 'Name: ' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.extendedProps.name) + '</div>'
111+
}
112+
if (mouseEnterInfo.event.extendedProps.name) {
113+
content += '<div>' + 'Notes: ' + LABKEY.Utils.encodeHtml(mouseEnterInfo.event.extendedProps.notes) + '</div>'
114+
}
115+
content += '</div>';
108116

109117

110118
$(mouseEnterInfo.el).popover({
111119
trigger: 'manual',
120+
placement: 'auto top',
112121
container: 'body',
113122
html:true,
114123
content: content,
115-
116124
});
117125

118126
$(mouseEnterInfo.el).popover('show');
@@ -767,7 +775,7 @@ $(function() {
767775
LABKEY.Query.selectRows({
768776
schemaName: 'targetedms',
769777
queryName: 'instrumentSchedule',
770-
columns: 'Id,startTime,endTime,name,notes,instrument/color,instrument/Id,project/Id, project/Title',
778+
columns: 'Id,startTime,endTime,name,notes,instrument/color,instrument/Id,project/Id,project/Title,CreatedBy/DisplayName',
771779
filterArray: [
772780
LABKEY.Filter.create('instrument', currentInstrument),
773781
],
@@ -781,6 +789,7 @@ $(function() {
781789
end: new Date(row.endTime),
782790
name: row.name,
783791
notes: row.notes,
792+
reservedBy: row['CreatedBy/DisplayName'],
784793
color: row['instrument/color'],
785794
project: row['project/Id'],
786795
title: row['project/Title']

0 commit comments

Comments
 (0)