refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
751
app/Models/Account/Account.php
Normal file
751
app/Models/Account/Account.php
Normal file
@@ -0,0 +1,751 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\User\User;
|
||||
use App\Models\Contact\Tag;
|
||||
use App\Models\Journal\Day;
|
||||
use App\Models\User\Module;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Contact\Call;
|
||||
use App\Models\Contact\Debt;
|
||||
use App\Models\Contact\Gift;
|
||||
use App\Models\Contact\Note;
|
||||
use App\Models\Contact\Task;
|
||||
use App\Traits\Subscription;
|
||||
use App\Models\Journal\Entry;
|
||||
use App\Models\Contact\Gender;
|
||||
use App\Models\Contact\Address;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\Contact\Message;
|
||||
use App\Models\Contact\Document;
|
||||
use App\Models\Contact\Reminder;
|
||||
use App\Models\Contact\LifeEvent;
|
||||
use App\Models\Instance\AuditLog;
|
||||
use App\Services\User\CreateUser;
|
||||
use App\Models\Contact\Occupation;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use App\Models\Contact\ContactField;
|
||||
use App\Models\Contact\Conversation;
|
||||
use App\Models\Contact\ReminderRule;
|
||||
use App\Models\Instance\SpecialDate;
|
||||
use App\Models\Journal\JournalEntry;
|
||||
use App\Models\Contact\LifeEventType;
|
||||
use App\Models\Contact\ReminderOutbox;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Contact\ContactFieldType;
|
||||
use App\Models\Contact\LifeEventCategory;
|
||||
use App\Models\Relationship\Relationship;
|
||||
use App\Models\Relationship\RelationshipType;
|
||||
use App\Models\Relationship\RelationshipTypeGroup;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use App\Services\Auth\Population\PopulateModulesTable;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use App\Services\Auth\Population\PopulateLifeEventsTable;
|
||||
use App\Services\Auth\Population\PopulateContactFieldTypesTable;
|
||||
|
||||
/**
|
||||
* @property int $reminders_count
|
||||
* @property int $notes_count
|
||||
* @property int $activities_count
|
||||
* @property int $gifts_count
|
||||
* @property int $tasks_count
|
||||
*/
|
||||
class Account extends Model
|
||||
{
|
||||
use Subscription, HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'number_of_invitations_sent',
|
||||
'api_key',
|
||||
'default_time_reminder_is_sent',
|
||||
'default_gender_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'has_access_to_paid_version_for_free' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the activity records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activities()
|
||||
{
|
||||
return $this->hasMany(Activity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function allContacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the addressBook's contacts.
|
||||
*
|
||||
* @param string|null $addressBookName
|
||||
* @return HasMany<Contact>
|
||||
*/
|
||||
public function contacts(string $addressBookName = null)
|
||||
{
|
||||
$contacts = $this->allContacts();
|
||||
|
||||
return $addressBookName
|
||||
? $contacts->addressBook($this->id, $addressBookName)
|
||||
: $contacts->addressBook();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invitations associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function invitations()
|
||||
{
|
||||
return $this->hasMany(Invitation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the debt records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function debts()
|
||||
{
|
||||
return $this->hasMany(Debt::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the gift records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function gifts()
|
||||
{
|
||||
return $this->hasMany(Gift::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the note records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function notes()
|
||||
{
|
||||
return $this->hasMany(Note::class)->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reminder records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function reminders()
|
||||
{
|
||||
return $this->hasMany(Reminder::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reminder outboxes records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function reminderOutboxes()
|
||||
{
|
||||
return $this->hasMany(ReminderOutbox::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function tasks()
|
||||
{
|
||||
return $this->hasMany(Task::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->hasMany(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function relationships()
|
||||
{
|
||||
return $this->hasMany(Relationship::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity statistics record associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activityStatistics()
|
||||
{
|
||||
return $this->hasMany(ActivityStatistic::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activityTypes()
|
||||
{
|
||||
return $this->hasMany(ActivityType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type category records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activityTypeCategories()
|
||||
{
|
||||
return $this->hasMany(ActivityTypeCategory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the task records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function entries()
|
||||
{
|
||||
return $this->hasMany(Entry::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import jobs records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function importjobs()
|
||||
{
|
||||
return $this->hasMany(ImportJob::class)->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import job reports records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function importJobReports()
|
||||
{
|
||||
return $this->hasMany(ImportJobReport::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tags records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function tags()
|
||||
{
|
||||
return $this->hasMany(Tag::class)->orderBy('name', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the calls records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function calls()
|
||||
{
|
||||
return $this->hasMany(Call::class)->orderBy('called_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Contact Field types records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function contactFieldTypes()
|
||||
{
|
||||
return $this->hasMany(ContactFieldType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Contact Field records associated with the contact.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function contactFields()
|
||||
{
|
||||
return $this->hasMany(ContactField::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Journal Entries records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function journalEntries()
|
||||
{
|
||||
return $this->hasMany(JournalEntry::class)->orderBy('date', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the special dates records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function specialDates()
|
||||
{
|
||||
return $this->hasMany(SpecialDate::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Days records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function days()
|
||||
{
|
||||
return $this->hasMany(Day::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Genders records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function genders()
|
||||
{
|
||||
return $this->hasMany(Gender::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Reminder Rules records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function reminderRules()
|
||||
{
|
||||
return $this->hasMany(ReminderRule::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship types records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function relationshipTypes()
|
||||
{
|
||||
return $this->hasMany(RelationshipType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship type groups records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function relationshipTypeGroups()
|
||||
{
|
||||
return $this->hasMany(RelationshipTypeGroup::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the modules records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function modules()
|
||||
{
|
||||
return $this->hasMany(Module::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Conversation records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function conversations()
|
||||
{
|
||||
return $this->hasMany(Conversation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Message records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return $this->hasMany(Message::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Document records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function documents()
|
||||
{
|
||||
return $this->hasMany(Document::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Life Event Category records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function lifeEventCategories()
|
||||
{
|
||||
return $this->hasMany(LifeEventCategory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Life Event Type records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function lifeEventTypes()
|
||||
{
|
||||
return $this->hasMany(LifeEventType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Life Event records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function lifeEvents()
|
||||
{
|
||||
return $this->hasMany(LifeEvent::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Photos records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function photos()
|
||||
{
|
||||
return $this->hasMany(Photo::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Weather records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function weathers()
|
||||
{
|
||||
return $this->hasMany(Weather::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Places records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function places()
|
||||
{
|
||||
return $this->hasMany(Place::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Addresses records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function addresses()
|
||||
{
|
||||
return $this->hasMany(Address::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Address Books records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function addressBooks()
|
||||
{
|
||||
return $this->hasMany(AddressBook::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Address Book Subscriptions records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function addressBookSubscriptions()
|
||||
{
|
||||
return $this->hasMany(AddressBookSubscription::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Company records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function companies()
|
||||
{
|
||||
return $this->hasMany(Company::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Occupation records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function occupations()
|
||||
{
|
||||
return $this->hasMany(Occupation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* * Get the Audit log records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function auditLogs()
|
||||
{
|
||||
return $this->hasMany(AuditLog::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the Activity Type table right after an account is
|
||||
* created.
|
||||
*/
|
||||
public function populateActivityTypeTable()
|
||||
{
|
||||
$defaultActivityTypeCategories = DB::table('default_activity_type_categories')->get();
|
||||
|
||||
foreach ($defaultActivityTypeCategories as $defaultActivityTypeCategory) {
|
||||
$activityTypeCategoryId = DB::table('activity_type_categories')->insertGetId([
|
||||
'account_id' => $this->id,
|
||||
'translation_key' => $defaultActivityTypeCategory->translation_key,
|
||||
]);
|
||||
|
||||
$defaultActivityTypes = DB::table('default_activity_types')
|
||||
->where('default_activity_type_category_id', $defaultActivityTypeCategory->id)
|
||||
->get();
|
||||
|
||||
foreach ($defaultActivityTypes as $defaultActivityType) {
|
||||
DB::table('activity_types')->insert([
|
||||
'account_id' => $this->id,
|
||||
'activity_type_category_id' => $activityTypeCategoryId,
|
||||
'translation_key' => $defaultActivityType->translation_key,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the default genders in a new account.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function populateDefaultGendersTable()
|
||||
{
|
||||
Gender::create(['type' => Gender::MALE, 'name' => trans('app.gender_male'), 'account_id' => $this->id]);
|
||||
Gender::create(['type' => Gender::FEMALE, 'name' => trans('app.gender_female'), 'account_id' => $this->id]);
|
||||
Gender::create(['type' => Gender::OTHER, 'name' => trans('app.gender_none'), 'account_id' => $this->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the default reminder rules in a new account.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function populateDefaultReminderRulesTable()
|
||||
{
|
||||
ReminderRule::create(['number_of_days_before' => 7, 'account_id' => $this->id, 'active' => 1]);
|
||||
ReminderRule::create(['number_of_days_before' => 30, 'account_id' => $this->id, 'active' => 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the default relationship types in a new account.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function populateRelationshipTypeGroupsTable($ignoreTableAlreadyMigrated = false)
|
||||
{
|
||||
$defaultRelationshipTypeGroups = DB::table('default_relationship_type_groups')->get();
|
||||
foreach ($defaultRelationshipTypeGroups as $defaultRelationshipTypeGroup) {
|
||||
if (! $ignoreTableAlreadyMigrated || $defaultRelationshipTypeGroup->migrated == 0) {
|
||||
DB::table('relationship_type_groups')->insert([
|
||||
'account_id' => $this->id,
|
||||
'name' => $defaultRelationshipTypeGroup->name,
|
||||
'delible' => $defaultRelationshipTypeGroup->delible,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the relationship types table based on the default ones.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function populateRelationshipTypesTable($migrateOnlyNewTypes = false)
|
||||
{
|
||||
if ($migrateOnlyNewTypes) {
|
||||
$defaultRelationshipTypes = DB::table('default_relationship_types')->where('migrated', 0)->get();
|
||||
} else {
|
||||
$defaultRelationshipTypes = DB::table('default_relationship_types')->get();
|
||||
}
|
||||
|
||||
foreach ($defaultRelationshipTypes as $defaultRelationshipType) {
|
||||
$defaultRelationshipTypeGroup = DB::table('default_relationship_type_groups')
|
||||
->where('id', $defaultRelationshipType->relationship_type_group_id)
|
||||
->first();
|
||||
|
||||
$relationshipTypeGroup = $this->getRelationshipTypeGroupByType($defaultRelationshipTypeGroup->name);
|
||||
|
||||
if ($relationshipTypeGroup) {
|
||||
RelationshipType::create([
|
||||
'account_id' => $this->id,
|
||||
'name' => $defaultRelationshipType->name,
|
||||
'name_reverse_relationship' => $defaultRelationshipType->name_reverse_relationship,
|
||||
'relationship_type_group_id' => $relationshipTypeGroup->id,
|
||||
'delible' => $defaultRelationshipType->delible,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new account and associate a new User.
|
||||
*
|
||||
* @param string $first_name
|
||||
* @param string $last_name
|
||||
* @param string $email
|
||||
* @param string $password
|
||||
* @param string $ipAddress
|
||||
* @return self
|
||||
*/
|
||||
public static function createDefault($first_name, $last_name, $email, $password, $ipAddress = null, $lang = null)
|
||||
{
|
||||
// create new account
|
||||
$account = new self;
|
||||
$account->api_key = Str::random(30);
|
||||
$account->created_at = now();
|
||||
$account->save();
|
||||
|
||||
try {
|
||||
// create the first user for this account
|
||||
$user = app(CreateUser::class)->execute([
|
||||
'account_id' => $account->id,
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'locale' => $lang,
|
||||
'ip_address' => $ipAddress,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
$account->delete();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$account->populateDefaultFields();
|
||||
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates all the default column that should be there when a new account
|
||||
* is created or reset.
|
||||
*/
|
||||
public function populateDefaultFields()
|
||||
{
|
||||
app(PopulateContactFieldTypesTable::class)->execute([
|
||||
'account_id' => $this->id,
|
||||
'migrate_existing_data' => true,
|
||||
]);
|
||||
|
||||
$this->populateDefaultGendersTable();
|
||||
$this->populateDefaultReminderRulesTable();
|
||||
$this->populateRelationshipTypeGroupsTable();
|
||||
$this->populateRelationshipTypesTable();
|
||||
$this->populateActivityTypeTable();
|
||||
|
||||
app(PopulateLifeEventsTable::class)->execute([
|
||||
'account_id' => $this->id,
|
||||
'migrate_existing_data' => true,
|
||||
]);
|
||||
|
||||
app(PopulateModulesTable::class)->execute([
|
||||
'account_id' => $this->id,
|
||||
'migrate_existing_data' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the RelationshipType object matching the given type.
|
||||
*
|
||||
* @param string $relationshipTypeName
|
||||
* @return RelationshipType|null
|
||||
*/
|
||||
public function getRelationshipTypeByType(string $relationshipTypeName)
|
||||
{
|
||||
return $this->relationshipTypes->where('name', $relationshipTypeName)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the RelationshipType object matching the given type.
|
||||
*
|
||||
* @param string $relationshipTypeGroupName
|
||||
* @return RelationshipTypeGroup|null
|
||||
*/
|
||||
public function getRelationshipTypeGroupByType(string $relationshipTypeGroupName)
|
||||
{
|
||||
return $this->relationshipTypeGroups->where('name', $relationshipTypeGroupName)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first available locale in an account. This gets the first user
|
||||
* in the account and reads his locale.
|
||||
*
|
||||
* @return string|null
|
||||
*
|
||||
* @throws ModelNotFoundException
|
||||
*/
|
||||
public function getFirstLocale(): ?string
|
||||
{
|
||||
try {
|
||||
$user = $this->users()->firstOrFail();
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $user->locale;
|
||||
}
|
||||
}
|
||||
160
app/Models/Account/Activity.php
Normal file
160
app/Models/Account/Activity.php
Normal file
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Traits\Journalable;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\Journal\JournalEntry;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Models\Instance\Emotion\Emotion;
|
||||
use App\Interfaces\IsJournalableInterface;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use App\Http\Resources\Contact\ContactShort as ContactShortResource;
|
||||
|
||||
/**
|
||||
* @property int|null $activity_type_id
|
||||
*/
|
||||
class Activity extends Model implements IsJournalableInterface
|
||||
{
|
||||
use Journalable, HasUuid;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'activities';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['happened_at'];
|
||||
|
||||
/**
|
||||
* The relations to eager load on every query.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $with = [
|
||||
'account',
|
||||
'type',
|
||||
'contacts',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the activity.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the activity.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->belongsToMany(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type record associated with the activity.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function type()
|
||||
{
|
||||
return $this->belongsTo(ActivityType::class, 'activity_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the activities journal entries.
|
||||
*/
|
||||
public function journalEntries()
|
||||
{
|
||||
return $this->morphMany(JournalEntry::class, 'journalable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the emotion records associated with the activity.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function emotions()
|
||||
{
|
||||
return $this->belongsToMany(Emotion::class, 'emotion_activity', 'activity_id', 'emotion_id')
|
||||
->withPivot('account_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the summary for this activity.
|
||||
*
|
||||
* @return string or null
|
||||
*/
|
||||
public function getSummary()
|
||||
{
|
||||
return $this->summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key of the title of the activity.
|
||||
*
|
||||
* @return string or null
|
||||
*/
|
||||
public function getTitle()
|
||||
{
|
||||
return $this->type ? $this->type->translation_key : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the contacts this activity is associated with.
|
||||
*/
|
||||
public function getContactsForAPI()
|
||||
{
|
||||
$attendees = $this->contacts->filter(function ($contact) {
|
||||
// This should not be possible!
|
||||
return $contact->account_id === $this->account_id;
|
||||
});
|
||||
|
||||
return ContactShortResource::collection($attendees);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the information about the activity for the journal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInfoForJournalEntry()
|
||||
{
|
||||
return [
|
||||
'type' => 'activity',
|
||||
'id' => $this->id,
|
||||
'activity_type' => (! is_null($this->type) ? $this->type->name : null),
|
||||
'summary' => $this->summary,
|
||||
'description' => $this->description,
|
||||
'day' => $this->happened_at->day,
|
||||
'day_name' => mb_convert_case(DateHelper::getShortDay($this->happened_at), MB_CASE_TITLE, 'UTF-8'),
|
||||
'month' => $this->happened_at->month,
|
||||
'month_name' => mb_convert_case(DateHelper::getShortMonth($this->happened_at), MB_CASE_UPPER, 'UTF-8'),
|
||||
'year' => $this->happened_at->year,
|
||||
'attendees' => $this->getContactsForAPI(),
|
||||
];
|
||||
}
|
||||
}
|
||||
39
app/Models/Account/ActivityStatistic.php
Normal file
39
app/Models/Account/ActivityStatistic.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ActivityStatistic extends Model
|
||||
{
|
||||
protected $table = 'activity_statistics';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'contact_id',
|
||||
'year',
|
||||
'count',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the activity statistic.
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the activity statistic.
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
}
|
||||
82
app/Models/Account/ActivityType.php
Normal file
82
app/Models/Account/ActivityType.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ActivityType extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
protected $table = 'activity_types';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'activity_type_category_id',
|
||||
'account_id',
|
||||
'translation_key',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the activity type.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type category record associated with the activity types.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(ActivityTypeCategory::class, 'activity_type_category_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity records associated with the activity type.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activities()
|
||||
{
|
||||
return $this->hasMany(Activity::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type's attribute.
|
||||
*/
|
||||
public function getNameAttribute($value)
|
||||
{
|
||||
if ($this->translation_key && ! $value) {
|
||||
return trans('people.activity_type_'.$this->translation_key);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all associated activities with this category type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function resetAssociationWithActivities()
|
||||
{
|
||||
foreach ($this->activities as $activity) {
|
||||
$activity->activity_type_id = null;
|
||||
$activity->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
63
app/Models/Account/ActivityTypeCategory.php
Normal file
63
app/Models/Account/ActivityTypeCategory.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ActivityTypeCategory extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
protected $table = 'activity_type_categories';
|
||||
|
||||
protected $appends = ['name'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'translation_key',
|
||||
'account_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the activity type group.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type records associated with the category.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function activityTypes()
|
||||
{
|
||||
return $this->hasMany(ActivityType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity type category's attribute.
|
||||
*
|
||||
* @return string
|
||||
* @psalm-suppress InvalidReturnStatement
|
||||
*/
|
||||
public function getNameAttribute($value)
|
||||
{
|
||||
if ($this->translation_key && ! $value) {
|
||||
return trans('people.activity_type_category_'.$this->translation_key);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
75
app/Models/Account/AddressBook.php
Normal file
75
app/Models/Account/AddressBook.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\User\User;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class AddressBook extends Model
|
||||
{
|
||||
use HasFactory, HasUuid;
|
||||
|
||||
protected $table = 'addressbooks';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'user_id',
|
||||
'name',
|
||||
'description',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the address book.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the address book.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all contacts for this address book.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
}
|
||||
175
app/Models/Account/AddressBookSubscription.php
Normal file
175
app/Models/Account/AddressBookSubscription.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\User\User;
|
||||
use function safe\json_decode;
|
||||
use function safe\json_encode;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Services\DavClient\Utils\Dav\DavClient;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class AddressBookSubscription extends Model
|
||||
{
|
||||
use HasFactory, HasUuid;
|
||||
|
||||
protected $table = 'addressbook_subscriptions';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'user_id',
|
||||
'address_book_id',
|
||||
'name',
|
||||
'uri',
|
||||
'capabilities',
|
||||
'username',
|
||||
'password',
|
||||
'readonly',
|
||||
'syncToken',
|
||||
'localSyncToken',
|
||||
'frequency',
|
||||
'last_synchronized_at',
|
||||
'active',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'last_synchronized_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'readonly' => 'boolean',
|
||||
'active' => 'boolean',
|
||||
'localSyncToken' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load account with every contact.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $with = [
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the subscription.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the subscription.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the addressbook record associated with the subscription.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function addressBook()
|
||||
{
|
||||
return $this->belongsTo(AddressBook::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get capabilities.
|
||||
*
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
public function getCapabilitiesAttribute($value)
|
||||
{
|
||||
return json_decode($value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set capabilities.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setCapabilitiesAttribute($value)
|
||||
{
|
||||
$this->attributes['capabilities'] = json_encode($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get password.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getPasswordAttribute($value)
|
||||
{
|
||||
return decrypt($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set password.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setPasswordAttribute($value)
|
||||
{
|
||||
$this->attributes['password'] = encrypt($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include active subscriptions.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('active', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new client.
|
||||
*
|
||||
* @return DavClient
|
||||
*/
|
||||
public function getClient(): DavClient
|
||||
{
|
||||
return app(DavClient::class)
|
||||
->setBaseUri($this->uri)
|
||||
->setCredentials($this->username, $this->password);
|
||||
}
|
||||
}
|
||||
21
app/Models/Account/ApiUsage.php
Normal file
21
app/Models/Account/ApiUsage.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ApiUsage extends Model
|
||||
{
|
||||
protected $table = 'api_usage';
|
||||
|
||||
/**
|
||||
* Log a request made through the API.
|
||||
*/
|
||||
public function log(\Illuminate\Http\Request $request)
|
||||
{
|
||||
$this->url = $request->fullUrl();
|
||||
$this->method = $request->getMethod();
|
||||
$this->client_ip = $request->getClientIp();
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
53
app/Models/Account/Company.php
Normal file
53
app/Models/Account/Company.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Models\Contact\Occupation;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Company extends Model
|
||||
{
|
||||
protected $table = 'companies';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'weather_json',
|
||||
'account_id',
|
||||
'name',
|
||||
'website',
|
||||
'number_of_employees',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the company.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Occupation records associated with the contact.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function occupations()
|
||||
{
|
||||
return $this->hasMany(Occupation::class);
|
||||
}
|
||||
}
|
||||
114
app/Models/Account/ExportJob.php
Normal file
114
app/Models/Account/ExportJob.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use App\Notifications\ExportAccountDone;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class ExportJob extends Model
|
||||
{
|
||||
use HasUuid, HasFactory;
|
||||
|
||||
public const EXPORT_TODO = 'todo';
|
||||
public const EXPORT_DOING = 'doing';
|
||||
public const EXPORT_DONE = 'done';
|
||||
public const EXPORT_FAILED = 'failed';
|
||||
|
||||
/**
|
||||
* Export as SQL format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const SQL = 'sql';
|
||||
|
||||
/**
|
||||
* Export as JSON format.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const JSON = 'json';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'uuid',
|
||||
'account_id',
|
||||
'user_id',
|
||||
'type',
|
||||
'status',
|
||||
'filesystem',
|
||||
'filename',
|
||||
'started_at',
|
||||
'ended_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'started_at',
|
||||
'ended_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the import job.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the import job.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the export job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function start(): void
|
||||
{
|
||||
$this->status = self::EXPORT_DOING;
|
||||
$this->started_at = now();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* End the export job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function end(): void
|
||||
{
|
||||
$this->status = self::EXPORT_DONE;
|
||||
$this->ended_at = now();
|
||||
$this->save();
|
||||
|
||||
$this->user->notify(new ExportAccountDone($this));
|
||||
}
|
||||
}
|
||||
311
app/Models/Account/ImportJob.php
Normal file
311
app/Models/Account/ImportJob.php
Normal file
@@ -0,0 +1,311 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Sabre\VObject\Reader;
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Helpers\AccountHelper;
|
||||
use Sabre\VObject\Component\VCard;
|
||||
use App\Services\VCard\ImportVCard;
|
||||
use League\Flysystem\UnableToReadFile;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use League\Flysystem\UnableToDeleteFile;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Sabre\VObject\Splitter\VCard as VCardReader;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property Account $account
|
||||
* @property int $account_id
|
||||
* @property User $user
|
||||
* @property int $user_id
|
||||
* @property bool $failed
|
||||
* @property string $failed_reason
|
||||
* @property string $filename
|
||||
* @property int $contacts_found
|
||||
* @property int $contacts_skipped
|
||||
* @property int $contacts_imported
|
||||
* @property \Illuminate\Support\Carbon|null $started_at
|
||||
* @property \Illuminate\Support\Carbon|null $ended_at
|
||||
*/
|
||||
class ImportJob extends Model
|
||||
{
|
||||
const VCARD_SKIPPED = true;
|
||||
const VCARD_IMPORTED = false;
|
||||
|
||||
protected $table = 'import_jobs';
|
||||
|
||||
/**
|
||||
* The physical vCard file on disk.
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
public $physicalFile;
|
||||
|
||||
/**
|
||||
* All individual entries in the vCard file.
|
||||
*
|
||||
* @var VCardReader
|
||||
*/
|
||||
public $entries = null;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['started_at', 'ended_at'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the import job.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the import job.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import jobs reports records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function importJobReports()
|
||||
{
|
||||
return $this->hasMany(ImportJobReport::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an import job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process($behaviour = ImportVCard::BEHAVIOUR_ADD)
|
||||
{
|
||||
$this->initJob();
|
||||
|
||||
if (! $this->failed && $this->getPhysicalFile()) {
|
||||
$this->getEntries();
|
||||
|
||||
$this->processEntries($behaviour);
|
||||
}
|
||||
|
||||
$this->deletePhysicalFile();
|
||||
|
||||
if (! $this->failed) {
|
||||
$this->endJob();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform preliminary steps to start the import job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function initJob(): void
|
||||
{
|
||||
if (AccountHelper::hasLimitations($this->account)) {
|
||||
$this->fail(trans('auth.not_authorized'));
|
||||
}
|
||||
|
||||
$this->started_at = now();
|
||||
$this->contacts_imported = 0;
|
||||
$this->contacts_skipped = 0;
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the steps to finalize the import job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function endJob(): void
|
||||
{
|
||||
$this->ended_at = now();
|
||||
$this->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the import job as failed.
|
||||
*
|
||||
* @param string $reason
|
||||
* @return void
|
||||
*/
|
||||
private function fail(string $reason): void
|
||||
{
|
||||
$this->failed = true;
|
||||
if (! $this->failed_reason) {
|
||||
$this->failed_reason = $reason;
|
||||
}
|
||||
$this->endJob();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the physical file (the vCard file).
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function getPhysicalFile(): bool
|
||||
{
|
||||
try {
|
||||
$this->physicalFile = Storage::disk(config('filesystems.default'))->readStream($this->filename);
|
||||
} catch (UnableToReadFile $exception) {
|
||||
$this->fail(trans('settings.import_vcard_file_not_found'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the physical file from the disk.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function deletePhysicalFile(): bool
|
||||
{
|
||||
try {
|
||||
if (Storage::disk(config('filesystems.default'))->delete($this->filename) === false) {
|
||||
$this->fail(trans('settings.import_vcard_file_not_found'));
|
||||
|
||||
return false;
|
||||
}
|
||||
} catch (UnableToDeleteFile $exception) {
|
||||
$this->fail(trans('settings.import_vcard_file_not_found'));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of matches in the vCard file.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function getEntries()
|
||||
{
|
||||
if ($this->physicalFile !== null) {
|
||||
$this->entries = new VCardReader($this->physicalFile, Reader::OPTION_FORGIVING + Reader::OPTION_IGNORE_INVALID_LINES);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process all entries contained in the vCard file.
|
||||
*
|
||||
* @param string $behaviour
|
||||
* @return void
|
||||
*/
|
||||
private function processEntries($behaviour = ImportVCard::BEHAVIOUR_ADD)
|
||||
{
|
||||
while (true) {
|
||||
try {
|
||||
/** @var VCard|null */
|
||||
$entry = $this->entries !== null ? $this->entries->getNext() : null;
|
||||
if (! $entry) {
|
||||
// file end
|
||||
break;
|
||||
}
|
||||
$this->contacts_found++;
|
||||
} catch (\Throwable $e) {
|
||||
$this->skipEntry('?', (string) $e);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->processSingleEntry($entry, $behaviour);
|
||||
}
|
||||
|
||||
if ($this->contacts_found == 0) {
|
||||
$this->fail(trans('settings.import_vcard_file_no_entries'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a single vCard entry.
|
||||
*
|
||||
* @param string|VCard $entry
|
||||
* @param string $behaviour
|
||||
* @return void
|
||||
*/
|
||||
private function processSingleEntry($entry, $behaviour = ImportVCard::BEHAVIOUR_ADD): void
|
||||
{
|
||||
try {
|
||||
$result = app(ImportVCard::class)->execute([
|
||||
'account_id' => $this->account_id,
|
||||
'user_id' => $this->user_id,
|
||||
'entry' => $entry,
|
||||
'behaviour' => $behaviour,
|
||||
]);
|
||||
} catch (ValidationException $e) {
|
||||
$this->fail(implode(',', $e->validator->errors()->all()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (Arr::has($result, 'error') && ! empty($result['error'])) {
|
||||
$this->skipEntry($result['name'], $result['reason']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->contacts_imported++;
|
||||
$this->fileImportJobReport($result['name'], self::VCARD_IMPORTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the current entry.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $reason
|
||||
* @return void
|
||||
*/
|
||||
private function skipEntry($name, $reason = null): void
|
||||
{
|
||||
$this->fileImportJobReport($name, self::VCARD_SKIPPED, $reason);
|
||||
$this->contacts_skipped++;
|
||||
}
|
||||
|
||||
/**
|
||||
* File an import job report for the current entry.
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $status
|
||||
* @param string $reason
|
||||
* @return void
|
||||
*/
|
||||
private function fileImportJobReport($name, $status, $reason = null): void
|
||||
{
|
||||
$importJobReport = new ImportJobReport;
|
||||
$importJobReport->account_id = $this->account_id;
|
||||
$importJobReport->user_id = $this->user_id;
|
||||
$importJobReport->import_job_id = $this->id;
|
||||
$importJobReport->contact_information = trim($name);
|
||||
$importJobReport->skipped = $status;
|
||||
$importJobReport->skip_reason = $reason;
|
||||
$importJobReport->save();
|
||||
}
|
||||
}
|
||||
59
app/Models/Account/ImportJobReport.php
Normal file
59
app/Models/Account/ImportJobReport.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property int $account_id
|
||||
* @property User $user
|
||||
* @property int $user_id
|
||||
* @property int $import_job_id
|
||||
* @property string $contact_information
|
||||
* @property bool $skipped
|
||||
* @property string $skip_reason
|
||||
*/
|
||||
class ImportJobReport extends Model
|
||||
{
|
||||
protected $table = 'import_job_reports';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the import job report.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the import job report.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the import job record associated with the gift.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function importJob()
|
||||
{
|
||||
return $this->belongsTo(ImportJob::class);
|
||||
}
|
||||
}
|
||||
45
app/Models/Account/Invitation.php
Normal file
45
app/Models/Account/Invitation.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property User $invitedBy
|
||||
* @property string $invitation_key
|
||||
*/
|
||||
class Invitation extends Model
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the invitation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the task.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function invitedBy()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'invited_by_user_id');
|
||||
}
|
||||
}
|
||||
110
app/Models/Account/Photo.php
Normal file
110
app/Models/Account/Photo.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
|
||||
class Photo extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'photos';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the photo.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contacts record associated with the photo.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->belongsToMany(Contact::class)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first contact record associated with the photo.
|
||||
*
|
||||
* @return Contact
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->contacts->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full path of the photo.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function url()
|
||||
{
|
||||
if (config('filesystems.default_visibility') === 'public') {
|
||||
return asset(StorageHelper::disk(config('filesystems.default'))->url($this->new_filename));
|
||||
}
|
||||
|
||||
return route('storage', ['file' => $this->new_filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data-url format of the photo.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function dataUrl(): ?string
|
||||
{
|
||||
try {
|
||||
$url = $this->new_filename;
|
||||
$file = StorageHelper::disk(config('filesystems.default'))->get($url);
|
||||
|
||||
return (string) Image::make($file)->encode('data-url');
|
||||
} catch (FileNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the model from the database.
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
Storage::disk(config('filesystems.default'))
|
||||
->delete($this->new_filename);
|
||||
} catch (FileNotFoundException $e) {
|
||||
// continue
|
||||
}
|
||||
|
||||
return parent::delete();
|
||||
}
|
||||
}
|
||||
126
app/Models/Account/Place.php
Normal file
126
app/Models/Account/Place.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Helpers\CountriesHelper;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* A Place is not the same as an address.
|
||||
* An Address in Monica is a way of contacting the contact. An Address is linked
|
||||
* to a Place. But Places can exist without the Address object.
|
||||
* Places will be linked to activities, for instance.
|
||||
*/
|
||||
class Place extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'places';
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the place.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Weather record associated with the place.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function weathers()
|
||||
{
|
||||
return $this->hasMany(Weather::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the address as a sentence.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getAddressAsString(): ?string
|
||||
{
|
||||
$address = '';
|
||||
|
||||
if (! is_null($this->street)) {
|
||||
$address = $this->street;
|
||||
}
|
||||
|
||||
if (! is_null($this->city)) {
|
||||
$address .= ' '.$this->city;
|
||||
}
|
||||
|
||||
if (! is_null($this->province)) {
|
||||
$address .= ' '.$this->province;
|
||||
}
|
||||
|
||||
if (! is_null($this->postal_code)) {
|
||||
$address .= ' '.$this->postal_code;
|
||||
}
|
||||
|
||||
if (! is_null($this->country)) {
|
||||
$address .= ' '.$this->getCountryName();
|
||||
}
|
||||
|
||||
if (empty($address)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// trim extra whitespaces inside the address
|
||||
return Str::of($address)->replaceMatches('/\s+/', ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the country of the place.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCountryName(): ?string
|
||||
{
|
||||
if ($this->country) {
|
||||
return CountriesHelper::get($this->country);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an URL for Google Maps for the place.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGoogleMapAddress()
|
||||
{
|
||||
$place = $this->getAddressAsString();
|
||||
$place = urlencode($place);
|
||||
|
||||
return "https://www.google.com/maps/place/{$place}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Google Maps url for the latitude/longitude.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGoogleMapsAddressWithLatitude()
|
||||
{
|
||||
return 'http://maps.google.com/maps?q='.$this->latitude.','.+$this->longitude;
|
||||
}
|
||||
}
|
||||
245
app/Models/Account/Weather.php
Normal file
245
app/Models/Account/Weather.php
Normal file
@@ -0,0 +1,245 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Weather extends Model
|
||||
{
|
||||
protected $table = 'weather';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'place_id',
|
||||
'weather_json',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'weather_json' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the weather data.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the place record associated with the weather data.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function place()
|
||||
{
|
||||
return $this->belongsTo(Place::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather code.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSummaryCodeAttribute(): ?string
|
||||
{
|
||||
$json = $this->weather_json;
|
||||
|
||||
// currently.icon: Darksky version
|
||||
if (! ($icon = Arr::get($json, 'currently.icon'))) {
|
||||
if (($text = Arr::get($json, 'current.condition.text')) === 'Partly cloudy') {
|
||||
$icon = ((bool) Arr::get($json, 'current.is_day')) ? 'partly-cloudy-day' : 'partly-cloudy-night';
|
||||
} else {
|
||||
$icon = (string) Str::of($text)->lower()->replace(' ', '-');
|
||||
}
|
||||
}
|
||||
|
||||
return $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather summary.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSummaryAttribute(): ?string
|
||||
{
|
||||
$summary_code = $this->summary_code;
|
||||
if (empty($summary_code)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (string) Str::of(trans('app.weather_'.$summary_code));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather location.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLocationAttribute(): ?string
|
||||
{
|
||||
return Arr::get($this->weather_json, 'location.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather update date.
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
public function getDateAttribute(): ?Carbon
|
||||
{
|
||||
if (($timestamp = Arr::get($this->weather_json, 'current.last_updated_epoch')) !== null) {
|
||||
return Carbon::createFromTimestamp($timestamp);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the weather icon.
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function getEmojiAttribute(): string
|
||||
{
|
||||
switch ($this->summary_code) {
|
||||
case 'sunny':
|
||||
case 'clear-day':
|
||||
$string = '🌞';
|
||||
break;
|
||||
case 'clear':
|
||||
case 'clear-night':
|
||||
$string = '🌃';
|
||||
break;
|
||||
case 'light-drizzle':
|
||||
case 'patchy-light-drizzle':
|
||||
case 'patchy-light-rain':
|
||||
case 'light-rain':
|
||||
case 'moderate-rain-at-times':
|
||||
case 'moderate-rain':
|
||||
case 'patchy-rain-possible':
|
||||
case 'heavy-rain-at-times':
|
||||
case 'heavy-rain':
|
||||
case 'light-freezing-rain':
|
||||
case 'moderate-or-heavy-freezing-rain':
|
||||
case 'light-sleet':
|
||||
case 'moderate-or-heavy-rain-shower':
|
||||
case 'light-rain-shower':
|
||||
case 'torrential-rain-shower':
|
||||
case 'rain':
|
||||
$string = '🌧️';
|
||||
break;
|
||||
case 'snow':
|
||||
case 'blowing-snow':
|
||||
case 'patchy-light-snow':
|
||||
case 'light-snow':
|
||||
case 'patchy-moderate-snow':
|
||||
case 'moderate-snow':
|
||||
case 'patchy-heavy-snow':
|
||||
case 'heavy-snow':
|
||||
case 'light-snow-showers':
|
||||
case 'moderate-or-heavy-snow-showers':
|
||||
$string = '❄️';
|
||||
break;
|
||||
case 'patchy-snow-possible':
|
||||
case 'patchy-sleet-possible':
|
||||
case 'moderate-or-heavy-sleet':
|
||||
case 'light-sleet-showers':
|
||||
case 'moderate-or-heavy-sleet-showers':
|
||||
case 'sleet':
|
||||
$string = '🌨️';
|
||||
break;
|
||||
case 'wind':
|
||||
$string = '💨';
|
||||
break;
|
||||
case 'fog':
|
||||
case 'mist':
|
||||
case 'blizzard':
|
||||
case 'freezing-fog':
|
||||
$string = '🌫️';
|
||||
break;
|
||||
case 'overcast':
|
||||
case 'cloudy':
|
||||
$string = '☁️';
|
||||
break;
|
||||
case 'partly-cloudy-day':
|
||||
$string = '⛅';
|
||||
break;
|
||||
case 'partly-cloudy-night':
|
||||
$string = '🎑';
|
||||
break;
|
||||
case 'freezing-drizzle':
|
||||
case 'heavy-freezing-drizzle':
|
||||
case 'patchy-freezing-drizzle-possible':
|
||||
case 'ice-pellets':
|
||||
case 'light-showers-of-ice-pellets':
|
||||
case 'moderate-or-heavy-showers-of-ice-pellets':
|
||||
$string = '🧊';
|
||||
break;
|
||||
case 'thundery-outbreaks-possible':
|
||||
case 'patchy-light-rain-with-thunder':
|
||||
case 'moderate-or-heavy-rain-with-thunder':
|
||||
case 'patchy-light-snow-with-thunder':
|
||||
case 'moderate-or-heavy-snow-with-thunder':
|
||||
$string = '⛈️';
|
||||
break;
|
||||
default:
|
||||
$string = '🌈';
|
||||
break;
|
||||
}
|
||||
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the temperature attribute.
|
||||
* Temperature is fetched in Celsius. It needs to be
|
||||
* converted to Fahrenheit depending on the user.
|
||||
*
|
||||
* @param string $scale
|
||||
* @return string
|
||||
*/
|
||||
public function temperature($scale = 'celsius')
|
||||
{
|
||||
$json = $this->weather_json;
|
||||
|
||||
$temperature = Arr::get($json, 'currently.temperature') ?? Arr::get($json, 'current.temp_c');
|
||||
|
||||
if ($scale === 'fahrenheit') {
|
||||
$temperature = Arr::get($json, 'current.temp_f', 9 / 5 * $temperature + 32);
|
||||
}
|
||||
|
||||
$temperature = round($temperature, 1);
|
||||
|
||||
$numberFormatter = new \NumberFormatter(App::getLocale(), \NumberFormatter::DECIMAL);
|
||||
|
||||
return $numberFormatter->format($temperature);
|
||||
}
|
||||
}
|
||||
76
app/Models/Contact/Address.php
Normal file
76
app/Models/Contact/Address.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Place;
|
||||
use App\Models\Account\Account;
|
||||
use App\Interfaces\LabelInterface;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* An Address is where the contact lives (or lived).
|
||||
* The actual address (street name etc…) is represented with a Place object.
|
||||
*/
|
||||
class Address extends Model implements LabelInterface
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* All of the relationships to be touched.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $touches = ['contact'];
|
||||
|
||||
protected $table = 'addresses';
|
||||
|
||||
/**
|
||||
* Get the account record associated with the address.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the address.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the place record associated with the address.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function place()
|
||||
{
|
||||
return $this->belongsTo(Place::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label associated with the contact.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function labels()
|
||||
{
|
||||
return $this->belongsToMany(ContactFieldLabel::class);
|
||||
}
|
||||
}
|
||||
81
app/Models/Contact/Call.php
Normal file
81
app/Models/Contact/Call.php
Normal file
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Instance\Emotion\Emotion;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property Contact $contact
|
||||
*/
|
||||
class Call extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['called_at'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'contact_called' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load with every call.
|
||||
*/
|
||||
protected $with = [
|
||||
'account',
|
||||
'contact',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the call.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the call.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the emotion records associated with the call.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function emotions()
|
||||
{
|
||||
return $this->belongsToMany(Emotion::class, 'emotion_call', 'call_id', 'emotion_id')
|
||||
->withPivot('account_id', 'contact_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
1600
app/Models/Contact/Contact.php
Normal file
1600
app/Models/Contact/Contact.php
Normal file
File diff suppressed because it is too large
Load Diff
95
app/Models/Contact/ContactField.php
Normal file
95
app/Models/Contact/ContactField.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Interfaces\LabelInterface;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class ContactField extends Model implements LabelInterface
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* All of the relationships to be touched.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $touches = ['contact'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the contact field.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the contact field.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label associated with the contact.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function labels()
|
||||
{
|
||||
return $this->belongsToMany(ContactFieldLabel::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type associated with the contact field.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contactFieldType()
|
||||
{
|
||||
return $this->belongsTo(ContactFieldType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include contact field of email type.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeEmail($query)
|
||||
{
|
||||
return $query->whereHas('contactFieldType', function ($query) {
|
||||
$query->where('type', '=', ContactFieldType::EMAIL);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include contact field of phone type.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopePhone($query)
|
||||
{
|
||||
return $query->whereHas('contactFieldType', function ($query) {
|
||||
$query->where('type', '=', ContactFieldType::PHONE);
|
||||
});
|
||||
}
|
||||
}
|
||||
44
app/Models/Contact/ContactFieldLabel.php
Normal file
44
app/Models/Contact/ContactFieldLabel.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property string $label
|
||||
* @property string $label_i18n
|
||||
*/
|
||||
class ContactFieldLabel extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'contact_field_labels';
|
||||
|
||||
/** @var array<string> */
|
||||
public static $standardLabels = [
|
||||
'home',
|
||||
'work',
|
||||
'cell',
|
||||
'fax',
|
||||
'pager',
|
||||
'main',
|
||||
'other',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the contact field type.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
}
|
||||
66
app/Models/Contact/ContactFieldType.php
Normal file
66
app/Models/Contact/ContactFieldType.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ContactFieldType extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'contact_field_types';
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'delible' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Email type contact field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const EMAIL = 'email';
|
||||
|
||||
/**
|
||||
* Phone type contact field.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const PHONE = 'phone';
|
||||
|
||||
/**
|
||||
* Get the account record associated with the contact field type.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the conversations associated with the contact field type.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function conversations()
|
||||
{
|
||||
return $this->hasMany(Conversation::class);
|
||||
}
|
||||
}
|
||||
68
app/Models/Contact/Conversation.php
Normal file
68
app/Models/Contact/Conversation.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBindingHasher as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Conversation extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['happened_at'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the conversation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the conversation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact field type record associated with the conversation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contactFieldType()
|
||||
{
|
||||
return $this->belongsTo(ContactFieldType::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Message records associated with the conversation.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function messages()
|
||||
{
|
||||
return $this->hasMany(Message::class);
|
||||
}
|
||||
}
|
||||
103
app/Models/Contact/Debt.php
Normal file
103
app/Models/Contact/Debt.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Traits\AmountFormatter;
|
||||
use App\Models\Settings\Currency;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\ModelBindingHasherWithContact as Model;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property Contact $contact
|
||||
* @property int $amount
|
||||
*
|
||||
* @method static Builder due()
|
||||
* @method static Builder owed()
|
||||
* @method static Builder inProgress()
|
||||
*/
|
||||
class Debt extends Model
|
||||
{
|
||||
use AmountFormatter, HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Eager load with every debt.
|
||||
*/
|
||||
protected $with = [
|
||||
'account',
|
||||
'contact',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the debt.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the debt.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currency record associated with the debt.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function currency()
|
||||
{
|
||||
return $this->belongsTo(Currency::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to unpaid/unreceived debt.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeInProgress(Builder $query)
|
||||
{
|
||||
return $query->where('status', 'inprogress');
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to due debt.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeDue(Builder $query)
|
||||
{
|
||||
return $query->where('in_debt', 'yes');
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to owed debt.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeOwed(Builder $query)
|
||||
{
|
||||
return $query->where('in_debt', 'no');
|
||||
}
|
||||
}
|
||||
110
app/Models/Contact/Document.php
Normal file
110
app/Models/Contact/Document.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Contracts\Filesystem\FileNotFoundException;
|
||||
|
||||
class Document extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'documents';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'number_of_downloads' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the document.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the document.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the download link.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDownloadLink(): string
|
||||
{
|
||||
if (config('filesystems.default_visibility') === 'public') {
|
||||
return asset(StorageHelper::disk(config('filesystems.default'))->url($this->new_filename));
|
||||
}
|
||||
|
||||
return route('storage', ['file' => $this->new_filename]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data-url format of the document.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function dataUrl(): ?string
|
||||
{
|
||||
try {
|
||||
$url = $this->new_filename;
|
||||
$file = StorageHelper::disk(config('filesystems.default'))->get($url);
|
||||
|
||||
return sprintf('data:%s;base64,%s',
|
||||
$this->mime_type,
|
||||
base64_encode($file)
|
||||
);
|
||||
} catch (FileNotFoundException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the model from the database.
|
||||
*
|
||||
* @return bool|null
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
try {
|
||||
Storage::disk(config('filesystems.default'))
|
||||
->delete($this->new_filename);
|
||||
} catch (FileNotFoundException $e) {
|
||||
// continue
|
||||
}
|
||||
|
||||
return parent::delete();
|
||||
}
|
||||
}
|
||||
99
app/Models/Contact/Gender.php
Normal file
99
app/Models/Contact/Gender.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Gender extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'type',
|
||||
'account_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Male type gender.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const MALE = 'M';
|
||||
|
||||
/**
|
||||
* Female type gender.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const FEMALE = 'F';
|
||||
|
||||
/**
|
||||
* Other type gender.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const OTHER = 'O';
|
||||
|
||||
/**
|
||||
* Unknown type gender.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const UNKNOWN = 'U';
|
||||
|
||||
/**
|
||||
* None type gender.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public const NONE = 'N';
|
||||
|
||||
public const LIST = ['M', 'F', 'O', 'U', 'N'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the gender.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact records associated with the gender.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->hasMany(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this gender the default account one?.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDefault(): bool
|
||||
{
|
||||
return $this->account->default_gender_id === $this->id;
|
||||
}
|
||||
}
|
||||
155
app/Models/Contact/Gift.php
Normal file
155
app/Models/Contact/Gift.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Photo;
|
||||
use App\Models\Account\Account;
|
||||
use App\Traits\AmountFormatter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property Contact $contact
|
||||
* @property Contact|null $recipient
|
||||
* @property string $name
|
||||
* @property string $comment
|
||||
* @property string $url
|
||||
* @property Contact $is_for
|
||||
*
|
||||
* @method static Builder offered()
|
||||
* @method static Builder isIdea()
|
||||
*/
|
||||
class Gift extends Model
|
||||
{
|
||||
use AmountFormatter, HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the gift.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the gift.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the gift.
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function recipient()
|
||||
{
|
||||
return $this->hasOne(Contact::class, 'id', 'is_for');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the photos record associated with the gift.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function photos()
|
||||
{
|
||||
return $this->belongsToMany(Photo::class)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to already offered gifts.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeOffered(Builder $query)
|
||||
{
|
||||
return $query->where('status', 'offered');
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to gifts at the idea stage.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeIsIdea(Builder $query)
|
||||
{
|
||||
return $query->where('status', 'idea');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the gift is meant for a particular member
|
||||
* of the contact's family.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasParticularRecipient()
|
||||
{
|
||||
return $this->is_for !== null && $this->is_for !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the recipient for the gift.
|
||||
*
|
||||
* @param int $value
|
||||
* @return void
|
||||
*/
|
||||
public function setRecipientAttribute($value): void
|
||||
{
|
||||
$this->attributes['is_for'] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the recipient for this gift.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getRecipientNameAttribute(): ?string
|
||||
{
|
||||
if ($this->hasParticularRecipient()) {
|
||||
$recipient = $this->recipient;
|
||||
if (! is_null($recipient)) {
|
||||
return $recipient->first_name;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
96
app/Models/Contact/LifeEvent.php
Normal file
96
app/Models/Contact/LifeEvent.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LifeEvent extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
protected $table = 'life_events';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'note',
|
||||
'happened_at',
|
||||
'account_id',
|
||||
'contact_id',
|
||||
'reminder_id',
|
||||
'life_event_type_id',
|
||||
'happened_at_month_unknown',
|
||||
'happened_at_day_unknown',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['happened_at'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'happened_at_month_unknown' => 'boolean',
|
||||
'happened_at_day_unknown' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the life event.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the life event.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the life event type record associated with the life event.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function lifeEventType()
|
||||
{
|
||||
return $this->belongsTo(LifeEventType::class, 'life_event_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reminder record associated with the life event.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function reminder()
|
||||
{
|
||||
return $this->belongsTo(Reminder::class);
|
||||
}
|
||||
}
|
||||
57
app/Models/Contact/LifeEventCategory.php
Normal file
57
app/Models/Contact/LifeEventCategory.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LifeEventCategory extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
protected $table = 'life_event_categories';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'account_id',
|
||||
'default_life_event_category_key',
|
||||
'core_monica_data',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'core_monica_data' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the life event category.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the life event type records associated with the category.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function lifeEventTypes()
|
||||
{
|
||||
return $this->hasMany(LifeEventType::class);
|
||||
}
|
||||
}
|
||||
68
app/Models/Contact/LifeEventType.php
Normal file
68
app/Models/Contact/LifeEventType.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class LifeEventType extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
protected $table = 'life_event_types';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'account_id',
|
||||
'life_event_category_id',
|
||||
'default_life_event_type_key',
|
||||
'core_monica_data',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'core_monica_data' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the life event type.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the life event category record associated with the life event type.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function lifeEventCategory()
|
||||
{
|
||||
return $this->belongsTo(LifeEventCategory::class, 'life_event_category_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Life event records associated with the life event Type.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function lifeEvents()
|
||||
{
|
||||
return $this->hasMany(LifeEvent::class);
|
||||
}
|
||||
}
|
||||
66
app/Models/Contact/Message.php
Normal file
66
app/Models/Contact/Message.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBindingHasher as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Message extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['written_at'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'written_by_me' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the message.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the message.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Conversation records associated with the message.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function conversation()
|
||||
{
|
||||
return $this->belongsTo(Conversation::class);
|
||||
}
|
||||
}
|
||||
124
app/Models/Contact/Note.php
Normal file
124
app/Models/Contact/Note.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\ModelBindingWithContact as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property Contact $contact
|
||||
* @property string $parsed_body
|
||||
* @property string $body
|
||||
* @property bool $is_favorited
|
||||
* @property \Illuminate\Support\Carbon|null $favorited_at
|
||||
*/
|
||||
class Note extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_favorited' => 'boolean',
|
||||
];
|
||||
|
||||
protected $dates = [
|
||||
'favorited_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'contact_id',
|
||||
'body',
|
||||
'is_favorited',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load with every note.
|
||||
*/
|
||||
protected $with = [
|
||||
'account',
|
||||
'contact',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the note.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the note.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit notes to favorited ones.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeFavorited(Builder $query)
|
||||
{
|
||||
return $query->where('is_favorited', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the description of a note.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getBody()
|
||||
{
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the activity date for this note.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedAt()
|
||||
{
|
||||
return DateHelper::getShortDate($this->created_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the activity and formats it for the email.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getContent()
|
||||
{
|
||||
return wordwrap($this->getBody(), 75);
|
||||
}
|
||||
}
|
||||
87
app/Models/Contact/Occupation.php
Normal file
87
app/Models/Contact/Occupation.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Account\Company;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Occupation extends Model
|
||||
{
|
||||
protected $table = 'occupations';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'contact_id',
|
||||
'company_id',
|
||||
'title',
|
||||
'description',
|
||||
'salary',
|
||||
'salary_unit',
|
||||
'currently_works_here',
|
||||
'start_date',
|
||||
'end_date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Valid value for salary unit.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $salaryUnits = [
|
||||
'year', 'month', 'week', 'day', 'hour',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'start_date' => 'datetime:Y-m-d',
|
||||
'end_date' => 'datetime:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the occupation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the occupation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the company record associated with the occupation.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function company()
|
||||
{
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
}
|
||||
61
app/Models/Contact/Pet.php
Normal file
61
app/Models/Contact/Pet.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Pet extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the pet.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the pet.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the pet.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function petCategory()
|
||||
{
|
||||
return $this->belongsTo(PetCategory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the name to null if it's an empty string.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setNameAttribute($value)
|
||||
{
|
||||
$this->attributes['name'] = $value ?: null;
|
||||
}
|
||||
}
|
||||
28
app/Models/Contact/PetCategory.php
Normal file
28
app/Models/Contact/PetCategory.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class PetCategory extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'pet_categories';
|
||||
|
||||
/**
|
||||
* Scope a query to only include pet categories that are considered `common`.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeCommon($query)
|
||||
{
|
||||
return $query->where('is_common', 1);
|
||||
}
|
||||
}
|
||||
199
app/Models/Contact/Reminder.php
Normal file
199
app/Models/Contact/Reminder.php
Normal file
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\User\User;
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\ModelBindingHasherWithContact as Model;
|
||||
|
||||
/**
|
||||
* A reminder has two states: active and inactive.
|
||||
* An inactive reminder is basically a one_time reminder that has already be
|
||||
* sent once and has been marked inactive so we don't schedule it again.
|
||||
*
|
||||
* @property string $next_expected_date_human_readable
|
||||
* @property string $next_expected_date
|
||||
*/
|
||||
class Reminder extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_birthday' => 'boolean',
|
||||
'delible' => 'boolean',
|
||||
'inactive' => 'boolean',
|
||||
'initial_date' => 'date:Y-m-d',
|
||||
];
|
||||
|
||||
/**
|
||||
* Valid value for frequency type.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $frequencyTypes = [
|
||||
'one_time', 'week', 'month', 'year',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Reminder Outbox records associated with the account.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function reminderOutboxes()
|
||||
{
|
||||
return $this->hasMany(ReminderOutbox::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include active reminders.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('inactive', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if this reminder is the contact's birthday reminder.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isBirthdayReminder(): bool
|
||||
{
|
||||
return $this->contact !== null
|
||||
&& $this->contact->birthday_reminder_id === $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next expected date for this reminder.
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
public function calculateNextExpectedDate($date = null)
|
||||
{
|
||||
if (is_null($date)) {
|
||||
$date = $this->initial_date;
|
||||
}
|
||||
|
||||
while ($date->isPast()) {
|
||||
$date = DateHelper::addTimeAccordingToFrequencyType($date, $this->frequency_type, $this->frequency_number);
|
||||
}
|
||||
|
||||
if ($date->isToday()) {
|
||||
$date = DateHelper::addTimeAccordingToFrequencyType($date, $this->frequency_type, $this->frequency_number);
|
||||
}
|
||||
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the next expected date using user timezone for this reminder.
|
||||
*
|
||||
* @return Carbon
|
||||
*/
|
||||
public function calculateNextExpectedDateOnTimezone()
|
||||
{
|
||||
$date = $this->initial_date;
|
||||
$date = Carbon::create($date->year, $date->month, $date->day, 0, 0, 0,
|
||||
DateHelper::getTimezone() ?? config('app.timezone'));
|
||||
|
||||
return $this->calculateNextExpectedDate($date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the reminder to be sent.
|
||||
*
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function schedule(User $user)
|
||||
{
|
||||
// remove any existing scheduled reminders
|
||||
$this->reminderOutboxes->each->delete();
|
||||
|
||||
// when should we send this reminder?
|
||||
$triggerDate = $this->calculateNextExpectedDate();
|
||||
|
||||
// schedule the reminder in the outbox, one for each user of the account
|
||||
ReminderOutbox::create([
|
||||
'account_id' => $this->account_id,
|
||||
'reminder_id' => $this->id,
|
||||
'user_id' => $user->id,
|
||||
'planned_date' => $triggerDate,
|
||||
'nature' => 'reminder',
|
||||
]);
|
||||
|
||||
$this->scheduleNotifications($triggerDate, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all the notifications that are supposed to be sent
|
||||
* 30 and 7 days prior to the actual reminder.
|
||||
*
|
||||
* @param Carbon $triggerDate
|
||||
* @param User $user
|
||||
* @return void
|
||||
*/
|
||||
public function scheduleNotifications(Carbon $triggerDate, User $user)
|
||||
{
|
||||
$date = $triggerDate->toDateString();
|
||||
$reminderRules = $this->account->reminderRules()->where('active', 1)->get();
|
||||
|
||||
foreach ($reminderRules as $reminderRule) {
|
||||
$datePrior = Carbon::createFromFormat('Y-m-d', $date)
|
||||
->subDays($reminderRule->number_of_days_before);
|
||||
|
||||
if ($datePrior->lessThanOrEqualTo(now())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ReminderOutbox::create([
|
||||
'account_id' => $this->account_id,
|
||||
'reminder_id' => $this->id,
|
||||
'user_id' => $user->id,
|
||||
'planned_date' => $datePrior->toDateString(),
|
||||
'nature' => 'notification',
|
||||
'notification_number_days_before' => $reminderRule->number_of_days_before,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
71
app/Models/Contact/ReminderOutbox.php
Normal file
71
app/Models/Contact/ReminderOutbox.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\User\User;
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\ModelBindingHasherWithContact as Model;
|
||||
|
||||
/**
|
||||
* @property Account $account
|
||||
* @property int $account_id
|
||||
* @property Contact $contact
|
||||
* @property User $user
|
||||
* @property int $user_id
|
||||
* @property Reminder|null $reminder
|
||||
* @property int $reminder_id
|
||||
* @property string $nature
|
||||
* @property \Illuminate\Support\Carbon|null $planned_date
|
||||
* @property int $notification_number_days_before
|
||||
*/
|
||||
class ReminderOutbox extends Model
|
||||
{
|
||||
protected $table = 'reminder_outbox';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'planned_date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reminder record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function reminder()
|
||||
{
|
||||
return $this->belongsTo(Reminder::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
38
app/Models/Contact/ReminderRule.php
Normal file
38
app/Models/Contact/ReminderRule.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class ReminderRule extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'reminder_rules';
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'active' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
}
|
||||
67
app/Models/Contact/Tag.php
Normal file
67
app/Models/Contact/Tag.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Tag extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'name_slug',
|
||||
'account_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the tag.
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contacts record associated with the tag.
|
||||
*/
|
||||
public function contacts()
|
||||
{
|
||||
return $this->belongsToMany(Contact::class)->withPivot('account_id')->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tags with the contact count.
|
||||
*/
|
||||
public static function contactsCount()
|
||||
{
|
||||
return DB::table('contact_tag')->selectRaw('COUNT(tag_id) AS contact_count, name, tag_id AS id')
|
||||
->join('tags', function ($join) {
|
||||
$join->on('tags.id', '=', 'contact_tag.tag_id')
|
||||
->on('tags.account_id', '=', 'contact_tag.account_id');
|
||||
})
|
||||
->join('contacts', function ($join) {
|
||||
$join->on('contacts.id', '=', 'contact_tag.contact_id')
|
||||
->on('contacts.account_id', '=', 'contact_tag.account_id');
|
||||
})
|
||||
->where([
|
||||
'tags.account_id' => auth()->user()->account_id,
|
||||
'contacts.address_book_id' => null,
|
||||
])
|
||||
->groupBy('tag_id')
|
||||
->get()
|
||||
->sortByCollator('name');
|
||||
}
|
||||
}
|
||||
101
app/Models/Contact/Task.php
Normal file
101
app/Models/Contact/Task.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Contact;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property Account $account
|
||||
* @property Contact|null $contact
|
||||
* @property string $title
|
||||
* @property string $description
|
||||
* @property string $uuid
|
||||
* @property bool $completed
|
||||
* @property \Carbon\Carbon|null $completed_at
|
||||
*
|
||||
* @method static Builder completed()
|
||||
* @method static Builder inProgress()
|
||||
*/
|
||||
class Task extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'completed_at',
|
||||
'archived_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'completed' => 'boolean',
|
||||
'archived' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load with every task.
|
||||
*/
|
||||
protected $with = [
|
||||
'account',
|
||||
'contact',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the task.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the task.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit tasks to completed ones.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeCompleted(Builder $query)
|
||||
{
|
||||
return $query->where('completed', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit tasks to in-progress ones.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeInProgress(Builder $query)
|
||||
{
|
||||
return $query->where('completed', false);
|
||||
}
|
||||
}
|
||||
90
app/Models/Instance/AuditLog.php
Normal file
90
app/Models/Instance/AuditLog.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance;
|
||||
|
||||
use App\Models\User\User;
|
||||
use function Safe\json_decode;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AuditLog extends Model
|
||||
{
|
||||
protected $table = 'audit_logs';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'author_id',
|
||||
'about_contact_id',
|
||||
'author_name',
|
||||
'action',
|
||||
'objects',
|
||||
'should_appear_on_dashboard',
|
||||
'audited_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'audited_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'should_appear_on_dashboard' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the Account record associated with the audit log.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the User record associated with the audit log.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function author()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Contact record associated with the audit log.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class, 'about_contact_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON object.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getObjectAttribute($value)
|
||||
{
|
||||
return json_decode($this->objects);
|
||||
}
|
||||
}
|
||||
27
app/Models/Instance/Cron.php
Normal file
27
app/Models/Instance/Cron.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Cron extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'command',
|
||||
'last_run',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'last_run' => 'datetime',
|
||||
];
|
||||
}
|
||||
71
app/Models/Instance/Emotion/Emotion.php
Normal file
71
app/Models/Instance/Emotion/Emotion.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance\Emotion;
|
||||
|
||||
use App\Models\Contact\Call;
|
||||
use App\Models\Account\Activity;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
/**
|
||||
* An emotion (ex: Adoration) is defined into 3 categories:
|
||||
* - Primary: Love
|
||||
* - Secondary: Affection
|
||||
* - Tertiary: Adoration.
|
||||
*/
|
||||
class Emotion extends Model
|
||||
{
|
||||
protected $table = 'emotions';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the primary emotion record associated with the emotion.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function primary()
|
||||
{
|
||||
return $this->belongsTo(PrimaryEmotion::class, 'emotion_primary_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secondary emotion record associated with the emotion.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function secondary()
|
||||
{
|
||||
return $this->belongsTo(SecondaryEmotion::class, 'emotion_secondary_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the call records associated with the emotion.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function calls()
|
||||
{
|
||||
return $this->belongsToMany(Call::class, 'emotion_call', 'emotion_id', 'call_id')
|
||||
->withPivot('account_id', 'contact_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the activity records associated with the emotion.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function activities()
|
||||
{
|
||||
return $this->belongsToMany(Activity::class, 'emotion_activity', 'emotion_id', 'activity_id')
|
||||
->withPivot('account_id')
|
||||
->withTimestamps();
|
||||
}
|
||||
}
|
||||
44
app/Models/Instance/Emotion/PrimaryEmotion.php
Normal file
44
app/Models/Instance/Emotion/PrimaryEmotion.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance\Emotion;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* An emotion (ex: Adoration) is defined into 3 categories:
|
||||
* - Primary: Love
|
||||
* - Secondary: Affection
|
||||
* - Tertiary: Adoration.
|
||||
*/
|
||||
class PrimaryEmotion extends Model
|
||||
{
|
||||
protected $table = 'emotions_primary';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the emotion records associated with the primary emotion.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function emotions()
|
||||
{
|
||||
return $this->hasMany(Emotion::class, 'emotion_primary_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the secondary records associated with the primary emotion.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function secondaries()
|
||||
{
|
||||
return $this->hasMany(SecondaryEmotion::class, 'emotion_primary_id');
|
||||
}
|
||||
}
|
||||
45
app/Models/Instance/Emotion/SecondaryEmotion.php
Normal file
45
app/Models/Instance/Emotion/SecondaryEmotion.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance\Emotion;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* An emotion (ex: Adoration) is defined into 3 categories:
|
||||
* - Primary: Love
|
||||
* - Secondary: Affection
|
||||
* - Tertiary: Adoration.
|
||||
*/
|
||||
class SecondaryEmotion extends Model
|
||||
{
|
||||
protected $table = 'emotions_secondary';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the primary emotion record associated with the secondary emotion.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function primary()
|
||||
{
|
||||
return $this->belongsTo(PrimaryEmotion::class, 'emotion_primary_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the emotion records associated with the secondary emotion.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function emotions()
|
||||
{
|
||||
return $this->hasMany(Emotion::class);
|
||||
}
|
||||
}
|
||||
21
app/Models/Instance/Instance.php
Normal file
21
app/Models/Instance/Instance.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Instance extends Model
|
||||
{
|
||||
/**
|
||||
* Once migrations have been run to add a new default contact field type,
|
||||
* we need to mark the field as being migrated so we don't create another
|
||||
* default contact field type if another migration will change this table
|
||||
* in the future.
|
||||
*/
|
||||
public function markDefaultContactFieldTypeAsMigrated()
|
||||
{
|
||||
DB::table('default_contact_field_types')
|
||||
->update(['migrated' => 1]);
|
||||
}
|
||||
}
|
||||
184
app/Models/Instance/SpecialDate.php
Normal file
184
app/Models/Instance/SpecialDate.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\DateHelper;
|
||||
use Illuminate\Support\Carbon;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* A special date is a date that is not necessarily based on a year that we know.
|
||||
* This happens when we add a birthdate for instance. It can be based:
|
||||
* * on a real date, where we know the day, month and year
|
||||
* * on a date where we just know the day and the month but not the year
|
||||
* * on an age (we know this person is 33 but we don't know his birthdate,
|
||||
* so we'll give an estimation).
|
||||
*
|
||||
* Instead of adding a lot of logic in the Contact table, we've decided to
|
||||
* create this class that will deal with this complexity.
|
||||
*
|
||||
* @property bool $is_age_based
|
||||
* @property bool $is_year_unknown
|
||||
* @property \Carbon\Carbon|null $date
|
||||
*/
|
||||
class SpecialDate extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'special_dates';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* All of the relationships to be touched.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $touches = ['contact'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = ['date'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'contact_id',
|
||||
'account_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'is_age_based' => 'boolean',
|
||||
'is_year_unknown' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the special date.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the special date.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a short version of the date, taking into account if the year is
|
||||
* unknown or not. This will return either `July 21` or `July 21, 2017`.
|
||||
*/
|
||||
public function toShortString()
|
||||
{
|
||||
if ($this->is_year_unknown) {
|
||||
return DateHelper::getShortDateWithoutYear($this->date);
|
||||
}
|
||||
|
||||
return DateHelper::getShortDate($this->date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the age that the date represents, if the date is set and if it's
|
||||
* not based on a year we don't know.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getAge(): ?int
|
||||
{
|
||||
if (is_null($this->date)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->is_year_unknown) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->date->diffInYears(now());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SpecialDate from an age.
|
||||
*
|
||||
* @param int $age
|
||||
*/
|
||||
public function createFromAge(int $age)
|
||||
{
|
||||
$this->is_age_based = true;
|
||||
$this->date = now(DateHelper::getTimezone())->subYears($age)->month(1)->day(1);
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SpecialDate from an actual date, that might not contain a year.
|
||||
*
|
||||
* @param int $year
|
||||
* @param int $month
|
||||
* @param int $day
|
||||
*/
|
||||
public function createFromDate(int $year, int $month, int $day)
|
||||
{
|
||||
// year 0 represents the `unknown` choice in the dropdown representing
|
||||
// the years
|
||||
if ($year != 0) {
|
||||
$date = Carbon::createFromDate($year, $month, $day);
|
||||
$this->is_year_unknown = false;
|
||||
} else {
|
||||
$date = Carbon::createFromDate(now()->year, $month, $day);
|
||||
$this->is_year_unknown = true;
|
||||
}
|
||||
|
||||
$this->date = $date;
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associates a special date to a contact.
|
||||
*
|
||||
* @param Contact $contact
|
||||
*/
|
||||
public function setToContact(Contact $contact)
|
||||
{
|
||||
$this->account_id = $contact->account_id;
|
||||
$this->contact_id = $contact->id;
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
9
app/Models/Instance/Statistic.php
Normal file
9
app/Models/Instance/Statistic.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Instance;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Statistic extends Model
|
||||
{
|
||||
}
|
||||
56
app/Models/Journal/Day.php
Normal file
56
app/Models/Journal/Day.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Journal;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Traits\Journalable;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use App\Interfaces\IsJournalableInterface;
|
||||
|
||||
class Day extends Model implements IsJournalableInterface
|
||||
{
|
||||
use Journalable, HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $dates = [
|
||||
'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the debt.
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the information of the Entry for the journal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInfoForJournalEntry()
|
||||
{
|
||||
return [
|
||||
'type' => 'day',
|
||||
'id' => $this->id,
|
||||
'rate' => $this->rate,
|
||||
'comment' => $this->comment,
|
||||
'date' => $this->date,
|
||||
'day' => $this->date->day,
|
||||
'day_name' => mb_convert_case(DateHelper::getShortDay($this->date), MB_CASE_TITLE, 'UTF-8'),
|
||||
'month' => $this->date->month,
|
||||
'month_name' => mb_convert_case(DateHelper::getShortMonth($this->date), MB_CASE_UPPER, 'UTF-8'),
|
||||
'year' => $this->date->year,
|
||||
'happens_today' => $this->date->isToday(),
|
||||
];
|
||||
}
|
||||
}
|
||||
83
app/Models/Journal/Entry.php
Normal file
83
app/Models/Journal/Entry.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Journal;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Traits\Journalable;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use App\Interfaces\IsJournalableInterface;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property \Carbon\Carbon $date
|
||||
*/
|
||||
class Entry extends Model implements IsJournalableInterface
|
||||
{
|
||||
use Journalable, HasUuid;
|
||||
|
||||
protected $table = 'entries';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'title',
|
||||
'post',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the entry.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Entry date.
|
||||
*
|
||||
* @param string $value
|
||||
* @return \Carbon\Carbon
|
||||
*/
|
||||
public function getDateAttribute($value)
|
||||
{
|
||||
// Default to created_at, but show journalEntry->date if the entry type is JournalEntry
|
||||
return $this->journalEntry ? $this->journalEntry->date : $this->created_at;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the information of the Entry for the journal.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getInfoForJournalEntry()
|
||||
{
|
||||
return [
|
||||
'type' => 'entry',
|
||||
'id' => $this->id,
|
||||
'title' => $this->title,
|
||||
'post' => $this->post,
|
||||
'day' => $this->date->day,
|
||||
'day_name' => mb_convert_case(DateHelper::getShortDay($this->date), MB_CASE_TITLE, 'UTF-8'),
|
||||
'month' => $this->date->month,
|
||||
'month_name' => mb_convert_case(DateHelper::getShortMonth($this->date), MB_CASE_UPPER, 'UTF-8'),
|
||||
'year' => $this->date->year,
|
||||
'date' => $this->date,
|
||||
'created_at' => DateHelper::getShortDateWithTime($this->created_at),
|
||||
];
|
||||
}
|
||||
}
|
||||
127
app/Models/Journal/JournalEntry.php
Normal file
127
app/Models/Journal/JournalEntry.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Journal;
|
||||
|
||||
use App\Helpers\DateHelper;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Interfaces\IsJournalableInterface;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property Account $account
|
||||
* @property User $invitedBy
|
||||
* @property int $account_id
|
||||
* @property IsJournalableInterface $journalable
|
||||
* @property int $journalable_id
|
||||
* @property string $journalable_type
|
||||
* @property \Carbon\Carbon|null $date
|
||||
*/
|
||||
class JournalEntry extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'journal_entries';
|
||||
|
||||
protected $dates = [
|
||||
'date',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load with every entry.
|
||||
*/
|
||||
protected $with = [
|
||||
'journalable',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get all of the owning "journal-able" models.
|
||||
*
|
||||
* @return MorphTo
|
||||
*/
|
||||
public function journalable()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account record associated with the journal entry.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new entry in the journal.
|
||||
*
|
||||
* @param \App\Interfaces\IsJournalableInterface $resourceToLog
|
||||
* @return self
|
||||
*/
|
||||
public static function add(IsJournalableInterface $resourceToLog): self
|
||||
{
|
||||
$journal = new self;
|
||||
$journal->account_id = $resourceToLog->account_id;
|
||||
$journal->date = now(DateHelper::getTimezone());
|
||||
if ($resourceToLog instanceof \App\Models\Account\Activity) {
|
||||
$journal->date = $resourceToLog->happened_at;
|
||||
} elseif ($resourceToLog instanceof \App\Models\Journal\Entry) {
|
||||
$journal->date = $resourceToLog->attributes['date'];
|
||||
}
|
||||
$journal->save();
|
||||
$resourceToLog->journalEntries()->save($journal);
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an entry in the journal.
|
||||
*
|
||||
* @param \App\Interfaces\IsJournalableInterface $resourceToLog
|
||||
* @return self
|
||||
*/
|
||||
public function edit(IsJournalableInterface $resourceToLog): self
|
||||
{
|
||||
if ($resourceToLog instanceof \App\Models\Journal\Entry) {
|
||||
$this->date = $resourceToLog->attributes['date'];
|
||||
}
|
||||
$this->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the information about the object represented by the Journal Entry.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getObjectData()
|
||||
{
|
||||
// Instantiating the object
|
||||
/** @var IsJournalableInterface */
|
||||
$correspondingObject = $this->journalable;
|
||||
|
||||
return $correspondingObject->getInfoForJournalEntry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter by real entry (day rate or journal entry).
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeEntry(Builder $query): Builder
|
||||
{
|
||||
return $query->where('journalable_type', '!=', 'App\Models\Account\Activity');
|
||||
}
|
||||
}
|
||||
27
app/Models/ModelBinding.php
Normal file
27
app/Models/ModelBinding.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class ModelBinding extends Model
|
||||
{
|
||||
/**
|
||||
* Resolve binding.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string|null $field
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function resolveRouteBinding($value, $field = null): ?Model
|
||||
{
|
||||
if (Auth::guest()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->where('account_id', Auth::user()->account_id)
|
||||
->where($this->getRouteKeyName(), $value)
|
||||
->firstOrFail();
|
||||
}
|
||||
}
|
||||
11
app/Models/ModelBindingHasher.php
Normal file
11
app/Models/ModelBindingHasher.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Hasher;
|
||||
use App\Interfaces\Hashing;
|
||||
|
||||
abstract class ModelBindingHasher extends ModelBinding implements Hashing
|
||||
{
|
||||
use Hasher;
|
||||
}
|
||||
11
app/Models/ModelBindingHasherWithContact.php
Normal file
11
app/Models/ModelBindingHasherWithContact.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Traits\Hasher;
|
||||
use App\Interfaces\Hashing;
|
||||
|
||||
abstract class ModelBindingHasherWithContact extends ModelBindingWithContact implements Hashing
|
||||
{
|
||||
use Hasher;
|
||||
}
|
||||
32
app/Models/ModelBindingWithContact.php
Normal file
32
app/Models/ModelBindingWithContact.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class ModelBindingWithContact extends Model
|
||||
{
|
||||
/**
|
||||
* Resolve binding with contact relation.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string|null $field
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public function resolveRouteBinding($value, $field = null): ?Model
|
||||
{
|
||||
/** @var \App\Models\Contact\Contact|null */
|
||||
$contact = Route::current()->parameter('contact');
|
||||
|
||||
if (Auth::guest() || is_null($contact)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->where('account_id', Auth::user()->account_id)
|
||||
->where('contact_id', $contact->id)
|
||||
->where($this->getRouteKeyName(), $value)
|
||||
->firstOrFail();
|
||||
}
|
||||
}
|
||||
99
app/Models/Relationship/Relationship.php
Normal file
99
app/Models/Relationship/Relationship.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Relationship;
|
||||
|
||||
use App\Traits\HasUuid;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* A relationship defines relations between contacts.
|
||||
*/
|
||||
class Relationship extends Model
|
||||
{
|
||||
use HasUuid;
|
||||
|
||||
/**
|
||||
* All of the relationships to be touched.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $touches = [
|
||||
'contactIs',
|
||||
'ofContact',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'contact_is',
|
||||
'of_contact',
|
||||
'relationship_type_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the relationship.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the relationship.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function contactIs()
|
||||
{
|
||||
return $this->belongsTo(Contact::class, 'contact_is');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record connected with the relationship.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function ofContact()
|
||||
{
|
||||
return $this->belongsTo(Contact::class, 'of_contact');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship type record associated with the relationship.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function relationshipType()
|
||||
{
|
||||
return $this->belongsTo(RelationshipType::class, 'relationship_type_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reverser relationship of this one.
|
||||
*
|
||||
* @return self|null
|
||||
*/
|
||||
public function reverseRelationship(): ?self
|
||||
{
|
||||
$reverseRelationshipType = $this->relationshipType->reverseRelationshipType();
|
||||
if ($reverseRelationshipType) {
|
||||
return self::where([
|
||||
'account_id'=> $this->account_id,
|
||||
'contact_is' => $this->of_contact,
|
||||
'of_contact' => $this->contact_is,
|
||||
'relationship_type_id' => $reverseRelationshipType->id,
|
||||
])->first();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
125
app/Models/Relationship/RelationshipType.php
Normal file
125
app/Models/Relationship/RelationshipType.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Relationship;
|
||||
|
||||
use App\Helpers\AccountHelper;
|
||||
use App\Models\Contact\Gender;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class RelationshipType extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'relationship_types';
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'delible' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relationship type group record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function relationshipTypeGroup()
|
||||
{
|
||||
return $this->belongsTo(RelationshipTypeGroup::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reverser relationship type of this one.
|
||||
*
|
||||
* @return self|null
|
||||
*/
|
||||
public function reverseRelationshipType()
|
||||
{
|
||||
return $this->account->getRelationshipTypeByType($this->name_reverse_relationship);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the i18n version of the name attribute, like "Significant other".
|
||||
*
|
||||
* @psalm-suppress InvalidReturnType
|
||||
* @psalm-suppress InvalidReturnStatement
|
||||
*
|
||||
* @param Contact $contact
|
||||
* @param bool $includeOpposite
|
||||
* @param string $gender
|
||||
* @return string|null|\Illuminate\Contracts\Translation\Translator
|
||||
*/
|
||||
public function getLocalizedName(Contact $contact = null, bool $includeOpposite = false, string $gender = null)
|
||||
{
|
||||
$defaultGender = AccountHelper::getDefaultGender($this->account);
|
||||
|
||||
if (is_null($gender)) {
|
||||
$gender = $defaultGender;
|
||||
}
|
||||
|
||||
$femaleVersion = trans('app.relationship_type_'.$this->name.'_female');
|
||||
$maleVersion = trans('app.relationship_type_'.$this->name.'_male');
|
||||
if ($maleVersion === 'app.relationship_type_'.$this->name.'_male') {
|
||||
$maleVersion = trans('app.relationship_type_'.$this->name);
|
||||
}
|
||||
|
||||
if (! is_null($contact)) {
|
||||
$maleVersionWithName = trans('app.relationship_type_'.$this->name.'_male_with_name', ['name' => $contact->name]);
|
||||
if ($maleVersionWithName === 'app.relationship_type_'.$this->name.'_male_with_name') {
|
||||
$maleVersionWithName = trans('app.relationship_type_'.$this->name.'_with_name');
|
||||
}
|
||||
$femaleVersionWithName = trans('app.relationship_type_'.$this->name.'_female_with_name', ['name' => $contact->name]);
|
||||
|
||||
// include the reverse of the relation in the string (masculine/feminine)
|
||||
// this is used in the dropdown of the relationship types when creating
|
||||
// or deleting a relationship.
|
||||
if ($includeOpposite) {
|
||||
// in some language, masculine and feminine version of a relationship type is the same.
|
||||
// we need to keep just one version in that case.
|
||||
if ($femaleVersion === $maleVersion) {
|
||||
// `Maazarin's significant other`
|
||||
return $maleVersionWithName;
|
||||
}
|
||||
|
||||
return $defaultGender === Gender::FEMALE ?
|
||||
// `Maazarin's aunt/uncle`
|
||||
$femaleVersionWithName.'/'.$maleVersion :
|
||||
// `Maazarin's uncle/aunt`
|
||||
$maleVersionWithName.'/'.$femaleVersion;
|
||||
} else {
|
||||
return $gender === Gender::FEMALE ?
|
||||
// `Maazarin's aunt`
|
||||
$femaleVersionWithName :
|
||||
// `Maazarin's uncle`
|
||||
$maleVersionWithName;
|
||||
}
|
||||
}
|
||||
|
||||
return $gender === Gender::FEMALE ?
|
||||
// `aunt`
|
||||
$femaleVersion :
|
||||
// `uncle`
|
||||
$maleVersion;
|
||||
}
|
||||
}
|
||||
38
app/Models/Relationship/RelationshipTypeGroup.php
Normal file
38
app/Models/Relationship/RelationshipTypeGroup.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Relationship;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class RelationshipTypeGroup extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $table = 'relationship_type_groups';
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'delible' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the reminder.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
}
|
||||
12
app/Models/Settings/Currency.php
Normal file
12
app/Models/Settings/Currency.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Settings;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Currency extends Model
|
||||
{
|
||||
protected $table = 'currencies';
|
||||
|
||||
public $timestamps = false;
|
||||
}
|
||||
24
app/Models/Settings/Term.php
Normal file
24
app/Models/Settings/Term.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Settings;
|
||||
|
||||
use App\Models\User\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Term extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* Get the user record associated with the term.
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class)->withPivot('user_id')->withTimestamps();
|
||||
}
|
||||
}
|
||||
54
app/Models/User/Changelog.php
Normal file
54
app/Models/User/Changelog.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use Parsedown;
|
||||
use App\Helpers\DateHelper;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Changelog extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $dates = [
|
||||
'created_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user records associated with the tag.
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return $this->belongsToMany(User::class)->withPivot('read', 'upvote')->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the markdown parsed description.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDescriptionAttribute($value)
|
||||
{
|
||||
return (new Parsedown())->text($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the created_at date in a friendly format.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCreatedAtAttribute($value)
|
||||
{
|
||||
return DateHelper::getShortDate($value);
|
||||
}
|
||||
}
|
||||
52
app/Models/User/Module.php
Normal file
52
app/Models/User/Module.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
/**
|
||||
* A module is a section of information that appears in the Contact sheet, like
|
||||
* 'Activities' or 'Notes'.
|
||||
*/
|
||||
class Module extends Model
|
||||
{
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'active' => 'boolean',
|
||||
'delible' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the module.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include modules that are active.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeActive($query)
|
||||
{
|
||||
return $query->where('active', true);
|
||||
}
|
||||
}
|
||||
33
app/Models/User/RecoveryCode.php
Normal file
33
app/Models/User/RecoveryCode.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use App\Models\ModelBinding as Model;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class RecoveryCode extends Model
|
||||
{
|
||||
protected $table = 'recovery_codes';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'user_id',
|
||||
'recovery',
|
||||
];
|
||||
|
||||
/**
|
||||
* Scope a query to only include unused code.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @return Builder
|
||||
*/
|
||||
public function scopeUnused($query)
|
||||
{
|
||||
return $query->where('used', 0);
|
||||
}
|
||||
}
|
||||
29
app/Models/User/SyncToken.php
Normal file
29
app/Models/User/SyncToken.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use App\Models\ModelBinding as Model;
|
||||
|
||||
class SyncToken extends Model
|
||||
{
|
||||
protected $table = 'synctoken';
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'account_id',
|
||||
'user_id',
|
||||
'name',
|
||||
'timestamp',
|
||||
];
|
||||
}
|
||||
308
app/Models/User/User.php
Normal file
308
app/Models/User/User.php
Normal file
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\User;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Traits\HasUuid;
|
||||
use App\Helpers\FormHelper;
|
||||
use App\Jobs\SendVerifyEmail;
|
||||
use App\Models\Settings\Term;
|
||||
use App\Models\Account\Account;
|
||||
use App\Models\Contact\Contact;
|
||||
use App\Helpers\ComplianceHelper;
|
||||
use App\Models\Settings\Currency;
|
||||
use Laravel\Passport\HasApiTokens;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Contracts\Translation\HasLocalePreference;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class User extends Authenticatable implements MustVerifyEmail, HasLocalePreference
|
||||
{
|
||||
use Notifiable, HasApiTokens, HasUuid;
|
||||
|
||||
/**
|
||||
* The attributes that aren't mass assignable.
|
||||
*
|
||||
* @var array<string>|bool
|
||||
*/
|
||||
protected $guarded = ['id'];
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'password',
|
||||
'timezone',
|
||||
'locale',
|
||||
'currency_id',
|
||||
'fluid_container',
|
||||
'temperature_scale',
|
||||
'name_order',
|
||||
'google2fa_secret',
|
||||
];
|
||||
|
||||
/**
|
||||
* Eager load account with every user.
|
||||
*/
|
||||
protected $with = ['account'];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for arrays.
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $hidden = [
|
||||
'password', 'remember_token', 'google2fa_secret',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be cast to native types.
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'profile_new_life_event_badge_seen' => 'boolean',
|
||||
'admin' => 'boolean',
|
||||
'fluid_container' => 'boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Available names order.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public const NAMES_ORDER = [
|
||||
'firstname_lastname',
|
||||
'lastname_firstname',
|
||||
'firstname_lastname_nickname',
|
||||
'firstname_nickname_lastname',
|
||||
'lastname_firstname_nickname',
|
||||
'lastname_nickname_firstname',
|
||||
'nickname_firstname_lastname',
|
||||
'nickname_lastname_firstname',
|
||||
'nickname_bracketed_firstname_lastname',
|
||||
'nickname',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the account record associated with the user.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo(Account::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contact record associated with the 'me' contact.
|
||||
*
|
||||
* @return HasOne
|
||||
*/
|
||||
public function me()
|
||||
{
|
||||
return $this->hasOne(Contact::class, 'id', 'me_contact_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the term records associated with the user.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
*/
|
||||
public function terms()
|
||||
{
|
||||
return $this->belongsToMany(Term::class)->withPivot('ip_address')->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recovery codes associated with the user.
|
||||
*
|
||||
* @return HasMany
|
||||
*/
|
||||
public function recoveryCodes()
|
||||
{
|
||||
return $this->hasMany(RecoveryCode::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currency for this user.
|
||||
*
|
||||
* @return BelongsTo
|
||||
*/
|
||||
public function currency()
|
||||
{
|
||||
return $this->belongsTo(Currency::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a default value just in case the sort order is empty.
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getContactsSortOrderAttribute($value): string
|
||||
{
|
||||
return ! empty($value) ? $value : 'firstnameAZ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the layout is fluid or not for the UI.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFluidLayout(): string
|
||||
{
|
||||
if ($this->fluid_container) {
|
||||
return 'container-fluid';
|
||||
} else {
|
||||
return 'container';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users's full name. The name is formatted according to the user's
|
||||
* preference, either "Firstname Lastname", or "Lastname Firstname".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getNameAttribute(): string
|
||||
{
|
||||
$completeName = '';
|
||||
|
||||
if (FormHelper::getNameOrderForForms($this) === 'firstname') {
|
||||
$completeName = $this->first_name;
|
||||
|
||||
if ($this->last_name !== '') {
|
||||
$completeName = $completeName.' '.$this->last_name;
|
||||
}
|
||||
} else {
|
||||
if ($this->last_name !== '') {
|
||||
$completeName = $this->last_name;
|
||||
}
|
||||
|
||||
$completeName = $completeName.' '.$this->first_name;
|
||||
}
|
||||
|
||||
return $completeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ecrypt the user's google_2fa secret.
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setGoogle2faSecretAttribute($value): void
|
||||
{
|
||||
$this->attributes['google2fa_secret'] = encrypt($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the user's google_2fa secret.
|
||||
*
|
||||
* @param string|null $value
|
||||
* @return string|null
|
||||
*/
|
||||
public function getGoogle2faSecretAttribute($value): ?string
|
||||
{
|
||||
return is_null($value) ? null : decrypt($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate if the user has accepted the most current terms and privacy.
|
||||
*
|
||||
* @param string|null $value
|
||||
* @return bool
|
||||
*/
|
||||
public function getPolicyCompliantAttribute($value): bool
|
||||
{
|
||||
return ComplianceHelper::isCompliantWithCurrentTerm($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether the user should be reminded at this time.
|
||||
* This is affected by the user settings regarding the hour of the day he
|
||||
* wants to be reminded.
|
||||
*
|
||||
* @param Carbon|null $date
|
||||
* @return bool
|
||||
*/
|
||||
public function isTheRightTimeToBeReminded($date)
|
||||
{
|
||||
if (is_null($date)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$now = now($this->timezone);
|
||||
$isTheRightTime = true;
|
||||
|
||||
// compare date with current date for the user
|
||||
if (! $date->isSameDay($now)) {
|
||||
$isTheRightTime = false;
|
||||
}
|
||||
|
||||
// compare current hour for the user with the hour they want to be
|
||||
// reminded as per the hour set on the profile
|
||||
if (! $now->isSameHour($this->account->default_time_reminder_is_sent)) {
|
||||
$isTheRightTime = false;
|
||||
}
|
||||
|
||||
return $isTheRightTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the email verification notification.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function sendEmailVerificationNotification(): void
|
||||
{
|
||||
/** @var int $count */
|
||||
$count = Account::count();
|
||||
if (config('monica.signup_double_optin') && $count > 1) {
|
||||
SendVerifyEmail::dispatch($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the preferred locale of the entity.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function preferredLocale()
|
||||
{
|
||||
return $this->locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try using a recovery code.
|
||||
*
|
||||
* @param string $recovery
|
||||
* @return bool
|
||||
*/
|
||||
public function recoveryChallenge(string $recovery): bool
|
||||
{
|
||||
$recoveryCodes = $this->recoveryCodes()->unused()->get();
|
||||
|
||||
foreach ($recoveryCodes as $recoveryCode) {
|
||||
if ($recoveryCode->recovery === $recovery) {
|
||||
$recoveryCode->used = true;
|
||||
$recoveryCode->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user