mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Implement delete handling and do more checks to make sure we dont get corrupted ical files
This commit is contained in:
parent
0b79b69d41
commit
ae42a5a37a
@ -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 {}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
|
||||
//TODO: Add support for recurring event : https://github.com/tplaner/When
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@ -95,16 +93,28 @@ class ImportTimesheetData extends Command {
|
||||
$this->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,14 +134,36 @@ 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) {
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,23 +219,6 @@ class ImportTimesheetData extends Command {
|
||||
$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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user