diff --git a/_ide_helper_models.php b/_ide_helper_models.php index c769bb15ec03..2c7606d3c901 100644 --- a/_ide_helper_models.php +++ b/_ide_helper_models.php @@ -39,10 +39,12 @@ namespace { * @property integer $public_id * @property string $custom_value1 * @property string $custom_value2 + * @property string $vat_number * @property-read \Account $account * @property-read \Illuminate\Database\Eloquent\Collection|\Invoice[] $invoices * @property-read \Illuminate\Database\Eloquent\Collection|\Payment[] $payments * @property-read \Illuminate\Database\Eloquent\Collection|\Contact[] $contacts + * @property-read \Illuminate\Database\Eloquent\Collection|\Project[] $projects * @property-read \Country $country * @property-read \Currency $currency * @property-read \Size $size @@ -74,6 +76,7 @@ namespace { * @method static \Illuminate\Database\Query\Builder|\Client wherePublicId($value) * @method static \Illuminate\Database\Query\Builder|\Client whereCustomValue1($value) * @method static \Illuminate\Database\Query\Builder|\Client whereCustomValue2($value) + * @method static \Illuminate\Database\Query\Builder|\Client whereVatNumber($value) * @method static \EntityModel scope($publicId = false, $accountId = false) */ class Client {} @@ -104,6 +107,7 @@ namespace { * @property integer $public_id * @property boolean $force_pdfjs * @property string $remember_token + * @property integer $news_feed_id * @property-read \Account $account * @property-read \Theme $theme * @method static \Illuminate\Database\Query\Builder|\User whereId($value) @@ -127,6 +131,7 @@ namespace { * @method static \Illuminate\Database\Query\Builder|\User wherePublicId($value) * @method static \Illuminate\Database\Query\Builder|\User whereForcePdfjs($value) * @method static \Illuminate\Database\Query\Builder|\User whereRememberToken($value) + * @method static \Illuminate\Database\Query\Builder|\User whereNewsFeedId($value) */ class User {} } @@ -441,6 +446,7 @@ namespace { * @property-read \Client $client * @property-read \Illuminate\Database\Eloquent\Collection|\InvoiceItem[] $invoice_items * @property-read \InvoiceStatus $invoice_status + * @property-read \InvoiceDesign $invoice_design * @property-read \Illuminate\Database\Eloquent\Collection|\Invitation[] $invitations * @method static \Illuminate\Database\Query\Builder|\Invoice whereId($value) * @method static \Illuminate\Database\Query\Builder|\Invoice whereClientId($value) @@ -806,8 +812,10 @@ namespace { * * @property integer $id * @property string $name + * @property string $javascript * @method static \Illuminate\Database\Query\Builder|\InvoiceDesign whereId($value) * @method static \Illuminate\Database\Query\Builder|\InvoiceDesign whereName($value) + * @method static \Illuminate\Database\Query\Builder|\InvoiceDesign whereJavascript($value) */ class InvoiceDesign {} } @@ -987,14 +995,18 @@ namespace { * @property string $end_date * @property float $hours * @property float $discount + * @property boolean $manualedit * @property string $org_code * @property string $org_created_at * @property string $org_updated_at + * @property string $org_deleted_at * @property string $org_start_date_timezone * @property string $org_end_date_timezone * @property string $org_data * @property string $import_error + * @property string $import_warning * @property string $updated_data + * @property string $updated_data_at * @property-read \Account $account * @property-read \User $user * @property-read \TimesheetEventSource $source @@ -1020,14 +1032,18 @@ namespace { * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereEndDate($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereHours($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereDiscount($value) + * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereManualedit($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgCode($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgUpdatedAt($value) + * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgDeletedAt($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgStartDateTimezone($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgEndDateTimezone($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereOrgData($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereImportError($value) + * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereImportWarning($value) * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereUpdatedData($value) + * @method static \Illuminate\Database\Query\Builder|\TimesheetEvent whereUpdatedDataAt($value) */ class TimesheetEvent {} } @@ -1039,6 +1055,7 @@ namespace { * @property integer $id * @property integer $user_id * @property integer $account_id + * @property integer $client_id * @property \Carbon\Carbon $created_at * @property \Carbon\Carbon $updated_at * @property \Carbon\Carbon $deleted_at @@ -1046,10 +1063,12 @@ namespace { * @property string $description * @property-read \Account $account * @property-read \User $user + * @property-read \Client $client * @property-read \Illuminate\Database\Eloquent\Collection|\ProjectCode[] $codes * @method static \Illuminate\Database\Query\Builder|\Project whereId($value) * @method static \Illuminate\Database\Query\Builder|\Project whereUserId($value) * @method static \Illuminate\Database\Query\Builder|\Project whereAccountId($value) + * @method static \Illuminate\Database\Query\Builder|\Project whereClientId($value) * @method static \Illuminate\Database\Query\Builder|\Project whereCreatedAt($value) * @method static \Illuminate\Database\Query\Builder|\Project whereUpdatedAt($value) * @method static \Illuminate\Database\Query\Builder|\Project whereDeletedAt($value) @@ -1109,6 +1128,12 @@ namespace { * @property boolean $custom_invoice_taxes1 * @property boolean $custom_invoice_taxes2 * @property string $vat_number + * @property string $invoice_design + * @property string $invoice_number_prefix + * @property integer $invoice_number_counter + * @property string $quote_number_prefix + * @property integer $quote_number_counter + * @property boolean $share_counter * @property-read \Illuminate\Database\Eloquent\Collection|\User[] $users * @property-read \Illuminate\Database\Eloquent\Collection|\Client[] $clients * @property-read \Illuminate\Database\Eloquent\Collection|\Invoice[] $invoices @@ -1167,6 +1192,12 @@ namespace { * @method static \Illuminate\Database\Query\Builder|\Account whereCustomInvoiceTaxes1($value) * @method static \Illuminate\Database\Query\Builder|\Account whereCustomInvoiceTaxes2($value) * @method static \Illuminate\Database\Query\Builder|\Account whereVatNumber($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereInvoiceDesign($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereInvoiceNumberPrefix($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereInvoiceNumberCounter($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereQuoteNumberPrefix($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereQuoteNumberCounter($value) + * @method static \Illuminate\Database\Query\Builder|\Account whereShareCounter($value) */ class Account {} } diff --git a/app/commands/ImportTimesheetData.php b/app/commands/ImportTimesheetData.php index 8a5acddaa4fd..edb5a277eb08 100644 --- a/app/commands/ImportTimesheetData.php +++ b/app/commands/ImportTimesheetData.php @@ -1,7 +1,5 @@ info("Find events in " . $event_source->name); file_put_contents("/tmp/" . $event_source->name . ".ical", $icalresponses[$i]); // FIXME: Remove $T->lap("Split on events for ".$event_source->name); + + // Check if the file is complete + if(!preg_match("/^\s*BEGIN:VCALENDAR/", $icalresponses[$i]) || !preg_match("/END:VCALENDAR\s*$/", $icalresponses[$i])) { + $this->error("Missing start or end of ical file"); + continue; + } + + // Extract all events from ical file if (preg_match_all('/BEGIN:VEVENT\r?\n(.+?)\r?\nEND:VEVENT/s', $icalresponses[$i], $icalmatches)) { $this->info("Found ".(count($icalmatches[1])-1)." events"); - $T->lap("Fetch all uids so we can do a quick lookup ".$event_source->name); + $T->lap("Fetch all uids and last updated at so we can do a quick lookup to find out if the event needs to be updated in the database".$event_source->name); $uids = []; - $event_source->events()->get(['uid', 'org_updated_at', 'updated_data_at'])->map(function($item) use(&$uids) { + $org_deleted = []; // Create list of events we know are deleted on the source, but still have in the db + $event_source->events()->withTrashed()->get(['uid', 'org_updated_at', 'updated_data_at', 'org_deleted_at'])->map(function($item) use(&$uids, &$org_deleted) { if($item->org_updated_at > $item->updated_data_at) { $uids[$item->uid] = $item->org_updated_at; } else { $uids[$item->uid] = $item->updated_data_at; } + if($item->org_deleted_at > '0000-00-00 00:00:00') { + $org_deleted[$item->uid] = $item->updated_data_at; + } }); $deleted = $uids; @@ -124,15 +134,37 @@ class ImportTimesheetData extends Command { list($codename, $tags, $title) = TimesheetUtils::parseEventSummary($data['summary']); if ($codename != null) { $event = TimesheetEvent::createNew($user); + + // Copy data to new object $event->uid = $data['uid']; + $event->summary = $title; + $event->org_data = $eventstr; + $event->org_code = $codename; + if(isset($data['description'])) { + $event->description = $data['description']; + } + $event->owner = $event_source->owner; + $event->timesheet_event_source_id = $event_source->id; + if (isset($codes[$codename])) { + $event->project_id = $codes[$codename]->project_id; + $event->project_code_id = $codes[$codename]->id; + } + if (isset($data['location'])) { + $event->location = $data['location']; + } + # Add RECURRENCE-ID to the UID to make sure the event is unique if (isset($data['recurrence-id'])) { $event->uid .= "::".$data['recurrence-id']; } - //FIXME: Bail on RRULE as we don't support that - + //TODO: Add support for recurring event, make limit on number of events created : https://github.com/tplaner/When + // Bail on RRULE as we don't support that + if(isset($event['rrule'])) { + die("Recurring event not supported: {$event['summary']} - {$event['dtstart']}"); + } + // Convert to DateTime objects foreach (['dtstart', 'dtend', 'created', 'last-modified'] as $key) { // Parse and create DataTime object from ICAL format @@ -142,10 +174,11 @@ class ImportTimesheetData extends Command { if ($dt == null || $dt < $unix_epoch) { if ($key == 'created' || $key == 'last-modified') { $dt = $unix_epoch; // Default to UNIX epoch - $event->import_error = "Could not parse date for $key: '" . $data[$key] . "' so default to UNIX Epoc\n"; + $event->import_warning = "Could not parse date for $key: '" . $data[$key] . "' so default to UNIX Epoc\n"; } else { - echo "Could not parse date for $key: '" . $data[$key] . "'\n"; - exit(255); // TODO: Bail on this event or write to error table + $event->import_error = "Could not parse date for $key: '" . $data[$key] . "' so default to UNIX Epoc\n"; + // TODO: Bail on this event or write to error table + die("Could not parse date for $key: '" . $data[$key] . "'\n"); } } @@ -185,24 +218,7 @@ class ImportTimesheetData extends Command { // Calculate number of hours $di = $event->end_date->diff($event->start_date); $event->hours = $di->h + $di->i / 60; - - // Copy data to new object - $event->org_data = $eventstr; - $event->summary = $title; - if(isset($data['description'])) { - $event->description = $data['description']; - } - $event->org_code = $codename; - $event->owner = $event_source->owner; - $event->timesheet_event_source_id = $event_source->id; - if (isset($codes[$codename])) { - $event->project_id = $codes[$codename]->project_id; - $event->project_code_id = $codes[$codename]->id; - } - if (isset($data['location'])) { - $event->location = $data['location']; - } - + // Check for events we already have if (isset($uids[$event->uid])) { // Remove from deleted list @@ -211,7 +227,7 @@ class ImportTimesheetData extends Command { // See if the event has been updated compared to the one in the database $db_event_org_updated_at = new DateTime($uids[$event->uid], new DateTimeZone('UTC')); - // Check if same or older version of new event so skip + // Check if same or older version of new event then skip if($event->org_updated_at <= $db_event_org_updated_at) { // SKIP @@ -224,7 +240,7 @@ class ImportTimesheetData extends Command { // Make sure it's more than the org_updated_at that has been changed if (count($changes) > 1) { - // Check if we have changed the event in the database or used it in a timesheet + // Check if we have manually changed the event in the database or used it in a timesheet if ($db_event->manualedit || $db_event->timesheet) { $this->info("Updated Data"); $db_event->updated_data = $event->org_data; @@ -268,18 +284,34 @@ class ImportTimesheetData extends Command { } } } - $this->info("Deleted ".count($deleted). " events"); + // Delete events in database that no longer exists in the source foreach($deleted as $uid => $lastupdated_date) { - // FIXME: Delete events in database - echo "$uid: $lastupdated_date\n"; + // Skip we already marked this a deleted + if(isset($org_deleted[$uid])) { + unset($deleted[$uid]); + continue; + } + // Delete or update event in db + $db_event = $event_source->events()->where('uid', $uid)->firstOrFail(); + if($db_event->timesheet_id === null && !$db_event->manualedit) { + // Hard delete if this event has not been assigned to a timesheet or have been manually edited + $db_event->forceDelete(); + + } else { + // Mark as deleted in source + $db_event->org_deleted_at = new DateTime('now', new DateTimeZone('UTC')); + $db_event->save(); + + } } + $this->info("Deleted ".count($deleted). " events"); } else { - // Parse error + // TODO: Parse error } } else { - // Curl Error + // TODO: Curl Error } } diff --git a/app/database/migrations/2014_10_06_103529_add_timesheets.php b/app/database/migrations/2014_10_06_103529_add_timesheets.php index 22d0f117cfed..879cd450402d 100644 --- a/app/database/migrations/2014_10_06_103529_add_timesheets.php +++ b/app/database/migrations/2014_10_06_103529_add_timesheets.php @@ -116,6 +116,7 @@ class AddTimesheets extends Migration { $t->string('org_code'); $t->timeStamp('org_created_at'); $t->timeStamp('org_updated_at'); + $t->timeStamp('org_deleted_at')->default('0000-00-00T00:00:00'); $t->string('org_start_date_timezone')->nullable(); $t->string('org_end_date_timezone')->nullable(); $t->text('org_data');