2017-07-25 59 views
0

短版本:誤差電子郵件更改通知使用項目跟蹤工具模板

我使用從項目跟蹤工具模板代碼來發送電子郵件顯示的字段(聯繫人名稱從改變的狀態的變化:比利 - >蘇珊)。

當我有一個字段是一個日期而不是一個字符串時,一切都完美的預期。如果我在代碼中有日期字段,我會得到以下錯誤:

'字符串'值預期用於模型'SystemOrdersHistory'中的'NewValue'字段,但找到對象。錯誤:'模型'SystemOrdersHistory'中'NewValue'字段的'字符串'值,但找到對象。在onSystemOrdersSave_(數據源:218)在models.SystemOrders.onSaveEvent:1

修改記錄:(錯誤): '字符串' 值預計關於 '的NewValue' 在模型 'SystemOrdersHistory' 字段,但發現對象。

(錯誤):'字符串'值預期爲模型'SystemOrdersHistory'中的'NewValue'字段,但找到對象。

任何幫助將不勝感激!


龍版

我使用下面的代碼(調整到適合我的模型和字段的名稱)。

每當我向函數「notifyAboutItemChanges_」函數和「onSystemOrdersSave_」函數添加一個Date字段(例如:DeliveryDate)時,我會收到有關「期待一個字符串,但找到一個對象」的錯誤。

注意:「歷史」模型中的OldValue和NewValue字段都是字符串。

通知服務器腳本:

/** 
* Sends email. 
* @param {!string} to - email address of a recipient. 
* @param {!string} subject - subject of email message. 
* @param {!string} body - body of email message. 
*/ 
function sendEmail_(to, subject, body) { 
    try { 
    MailApp.sendEmail({ 
     to: to, 
     subject: subject, 
     htmlBody: body, 
     noReply: true 
    }); 
    } catch (e) { 
    // Suppressing errors in email sending because email notifications 
    // are not critical for the functioning of the app. 
    console.error(JSON.stringify(e)); 
    } 
} 


/** 
* Sends email notification about recent project item changes to item owner 
*  and assignee. 
* @param {!Array<ItemHistory>} changes - list of recent project item changes. 
*/ 
function notifyAboutItemChanges_(changes) { 
    if (!changes || changes.length < 2) { 
    return; 
    } 

    var settings = getAppSettingsRecord_()[0]; 

    if (!settings.EnableEmailNotifications) { 
    return; 
    } 

    var data = { 
    appUrl: settings.AppUrl, 
    itemShowName: changes[0].ShowName, 
    itemUsersPosition: changes[0].UsersPosition, 
    itemDeliveryInfo: changes[0].DeliveryInfo, 
    itemDeliveryDate: changes[0].DeliveryDate, 
    itemKey: changes[0]._key, 
    itemName: changes[0].Name, 
    modifiedBy: changes[0].ModifiedBy, 
    changes: changes 
    }; 

    // Email subject. 
    var subjectTemplate = 
     HtmlService.createTemplate(settings.NotificationEmailSubject); 

    subjectTemplate.data = data; 

    var subject = subjectTemplate.evaluate().getContent(); 

    // Email body. 
    var emailTemplate = 
     HtmlService.createTemplate(settings.NotificationEmailBody); 

    emailTemplate.data = data; 

    var htmlBody = emailTemplate.evaluate().getContent(); 

    sendEmail_('[email protected]', subject, htmlBody); 

數據源服務器腳本:

/** 
* Item key URL parameter. 
*/ 
var ITEM_KEY = 'itemKey'; 


/** 
* Checks that Application Settings record already exists. 
*  Otherwise creates a new one. 
* @return {!Array<AppSettings>} app settings record as an array. 
*/ 
function getAppSettingsRecord_() { 
    var newQuery = app.models.AppSettings.newQuery(); 
    var settingsRecords = newQuery.run(); 

    if (settingsRecords.length > 1) { 
    console.warn('There is more than one(%s) App Settings entries' + 
       'in the database', settingsRecords.length); 
    } 

    if (settingsRecords.length === 0) { 
    var settingsRecord = app.models.AppSettings.newRecord(); 

    settingsRecord.AppUrl = ScriptApp.getService().getUrl(); 
    settingsRecord.NotificationEmailSubject = 
     'A change has been made to <?= data.itemShowName?>: <?= data.itemUsersPosition?>'; 

    settingsRecord.NotificationEmailBody = 
     'Hello!\n<br/>\n<p><b><?= data.modifiedBy ?></b> ' + 
     'made the following changes: </p>\n' + 
     '<? for (var i = 1; i < data.changes.length; i++) {\n' + 
     '\tvar change = data.changes[i]; ?>\n' + 
     '\t<b><?= change.FieldName ?>: </b>\n' + 
     '\t<? if (change.FieldName === "Comment") { ?>\n' + 
     '\t\t<div style="white-space: pre-line;"><?= change.NewValue ?></div>' + 
     '\n\t<? } else { ?>\n ' + 
     '\t\t<?= change.OldValue ?> &#8594; <?= change.NewValue ?>' + 
     '\n\t<? } ?>\n\t<br/>\n' + 
     '<? } ?>\n<br/>\n' + 
     '<a href="<?= data.appUrl ?>?' + ITEM_KEY + '=<?= data.itemKey ?>' + 
     '#EditItem" target="_blank">Go to the project item</a>'; 

    app.saveRecords([settingsRecord]); 

    return [settingsRecord]; 
    } else { 
    return settingsRecords; 
    } 
} 

/** 
* Populates project record with required data on project create event. 
* @param {!Project} project - project being created. 
*/ 
function onProjectCreate_(project) { 
    var date = new Date(); 

    project.CreatedDate = date; 
    project.ModifiedDate = date; 
    project.ModifiedBy = currentUserEmail_(); 
} 


/** 
* Audits project on changes. 
* @param {!Project} project - project being modified. 
*/ 
function onProjectSave_(project) { 
    project.ModifiedDate = new Date(); 
    project.ModifiedBy = currentUserEmail_(); 
} 


/** 
* Populates project item with required data on item create event, adds 
*  comment entry to the project item history. 
* @param {!SystemOrders} SystemOrders - project item being created. 
*/ 
function onSystemOrdersCreate_(SystemOrders) { 
    var date = new Date(); 
    var editor = currentUserEmail_(); 

    if (SystemOrders.Comment) { 
    SystemOrders.Comment = SystemOrders.Comment.trim(); 
    } 

    SystemOrders.CreatedDate = date; 
    SystemOrders.Owner = editor; 
    SystemOrders.ModifiedDate = date; 
    SystemOrders.ModifiedBy = editor; 

    if (SystemOrders.Comment) { 
    var history = app.models.SystemOrdersHistory.newRecord(); 

    history.CreatedBy = currentUserEmail_(); 
    history.CreatedDate = new Date(); 
    history.FieldName = 'Comment'; 
    history.NewValue = SystemOrders.Comment; 

    app.saveRecords([history]); 

    SystemOrders.History.push(history); 
    } 

} 


/** 
* Calculates history entries sum for {Array<SystemOrders>}. 
* @param {!number} historySum - the accumulated number of history entries 
*  previously returned in the last invocation of the callback, or 
*  initialValue, if supplied. 
* @param {!SystemOrders} SystemOrders - the current {SystemOrders} being 
*  processed in the array. 
* @return {!number} history entries sum. 
*/ 
function sumHistory_(historySum, SystemOrders) { 
    return historySum + SystemOrders.History.length; 
} 


/** 
* Calculates potential project deletion impact. 
* Throws an error if there is no project with the key provided. 
* @param {!string} projectKey - project key to calculate deletion impact. 
*/ 
function getDeleteProjectImpact(projectKey) { 
    var projectQuery = app.models.Project.newQuery(); 

    projectQuery.prefetch.Items._add(); 
    projectQuery.prefetch.Items.History._add(); 
    projectQuery.filters._key._equals = projectKey; 

    var projects = projectQuery.run(); 

    if (projects.length === 0) { 
    throw new Error('Project with key ' + projectKey + ' was not found.'); 
    } 

    var SystemOrderss = projects[0].Items; 

    return { 
    affectedItems: SystemOrderss.length, 
    affectedHistory: SystemOrderss.reduce(sumHistory_, 0) 
    }; 
} 


/** 
* Checks that project item readonly fields were not modified. 
* Throws an error if user attempts to modify read only fields. 
* @param {!SystemOrders} record - modified project item. 
* @param {!SystemOrders} oldRecord - project item before modification. 
*/ 
function validateItemChange_(record, oldRecord) { 
    var readonlyFields = [ 
    'CreatedDate', 
    'ModifiedBy', 
    'ModifiedDate', 
    'Owner' 
    ]; 

    for (var i = 0; i < readonlyFields.length; i++) { 
    var field = readonlyFields[i]; 
    var newValue = record[field]; 
    var oldValue = oldRecord[field]; 
    var isDate = newValue instanceof Date && oldValue instanceof Date; 

    if (isDate === true) { 
     newValue = record[field].getDate(); 
     oldValue = oldRecord[field].getDate(); 
    } 

    if (newValue === oldValue) { 
     continue; 
    } 

    throw new Error(field + ' field is read only'); 
    } 
} 


/** 
* Handles project item change event, creates history entries for each changed 
*  field. 
* @param {!SystemOrders} record - modified project item. 
* @param {!SystemOrders} oldRecord - project item before modification. 
*/ 
function onSystemOrdersSave_(record, oldRecord) { 
    validateItemChange_(record, oldRecord); 

    var editableFields = [ 
    'ShowName', 
    'UsersPosition', 
    'DeliveryInfo', 
    'DeliveryDate' 
    ]; 

    var editor = currentUserEmail_(); 
    var date = new Date(); 
    var changes = [record]; 

    record.ModifiedBy = editor; 
    record.ModifiedDate = date; 

    for (var i = 0; i < editableFields.length; i++) { 
    var field = editableFields[i]; 
    var newValue = record[field]; 
    var oldValue = oldRecord[field]; 

    if (newValue !== oldValue) { 
     var history = app.models.SystemOrdersHistory.newRecord(); 

     history.Item = record; 
     history.CreatedBy = editor; 
     history.CreatedDate = date; 
     history.FieldName = field; 
     history.NewValue = newValue; 
     history.OldValue = oldValue; 

     changes.push(history); 
    } 
    } 

    app.saveRecords(changes); 

    notifyAboutItemChanges_(changes); 
} 


/** 
* Counts project items by some grouping criteria(field). 
* @param {!string} projectKey - project key to calculate stats. 
* @param {!string} grouping - project item field to group items by. 
* @param {!Array<string>} groupingValues - possible field values. 
* @return {!Array<SystemOrderssBreakdown>} grouped project items counts. 
*/ 
function getSystemOrderssBreakdown_(projectKey, grouping, groupingValues) { 
    if (!grouping || !groupingValues || groupingValues.length === 0) { 
    return []; 
    } 

    var itemsQuery = app.models.SystemOrders.newQuery(); 

    itemsQuery.prefetch.Project._add(); 
    itemsQuery.filters.Project._key._equals = projectKey; 

    var items = itemsQuery.run(); 

    if (items.length === 0) { 
    return []; 
    } 

    var records = []; 
    var map = {}; 

    for (var i = 0; i < items.length; i++) { 
    var itemGrouping = items[i][grouping]; 

    if (!map[itemGrouping]) { 
     map[itemGrouping] = 0; 
    } 

    map[itemGrouping]++; 
    } 

    for (i = 0; i < groupingValues.length; i++) { 
    var breakdownRecord = app.models.SystemOrderssBreakdown.newRecord(); 
    var groupingValue = groupingValues[i]; 

    breakdownRecord.Grouping = groupingValue; 
    breakdownRecord.ItemsCount = map[groupingValue] || 0; 

    records.push(breakdownRecord); 
    } 

    return records; 
} 

回答

3

它失敗在這裏:

// history.NewValue and history.OldValue are strings 
// newValue and oldValue can be of any type (Boolean, Number, Date, 
// but not a relation as of now) 
// You are getting an exception because you are not casting types 
history.NewValue = newValue; 
history.OldValue = oldValue; 
  1. 您可以通過添加每個可能的領域修復鍵入您的歷史模型(NewStrin gValue,NewDateValue,NewBool​​Value,NewNumberValue,OldStringValue ...)。通過這種方法,您將獲得強大的打字功能,但您的代碼和用戶界面將變得非常複雜...

  2. 您可以將所有字段的歷史記錄存儲爲字符串(如現在所做的那樣)這種情況下,你需要考慮的格式和本地化提前:

function fieldToString(field, fieldValue) { 
    // TODO: pass field metadata to individually handle 
    // different data types. 
    return fieldValue !== null ? fieldValue.toString() : null; 
} 

... 
history.NewValue = fieldToString(field, newValue); 
history.OldValue = fieldToString(field, oldValue); 
... 
+0

帕維爾,一如既往感謝您的幫助。所以我試着做選項#2,但我有兩個問題。 1.有時候存在數據源保存,系統將DeliveryDate視爲已更改(即使它未更改)。因此,在舊價值和新價值之間進行驗證的東西正變得混亂。 2.是否有一種方法可以在validateItemChange_函數下執行某些操作(如果我正確理解其功能),它會檢查值是否爲日期並允許我調整格式? –

+1

所以我實際上找到了一個解決上述兩個問題的選項。基本上我使用validateItemChange_中的if(isDate === true)。感謝您的幫助! –