refactor: shopping lists -> checklists

This commit is contained in:
2026-04-07 00:28:17 +03:00
parent 9ef00fcb15
commit 3887f751d8
26 changed files with 1690 additions and 1690 deletions

View File

@@ -7,19 +7,19 @@ declare(strict_types=1);
namespace OCA\Pantry\BackgroundJob;
use OCA\Pantry\Service\ShoppingListService;
use OCA\Pantry\Service\ChecklistService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use Psr\Log\LoggerInterface;
/**
* Periodically reopens recurring shopping-list items whose next_due_at has
* Periodically reopens recurring checklist items whose next_due_at has
* passed, so they appear unchecked without waiting for a user to open the list.
*/
class ReopenRecurringItemsJob extends TimedJob {
public function __construct(
ITimeFactory $time,
private ShoppingListService $lists,
private ChecklistService $lists,
private LoggerInterface $logger,
) {
parent::__construct($time);

View File

@@ -13,7 +13,7 @@ use OCA\Pantry\ResponseDefinitions;
use OCA\Pantry\Service\CategoryService;
use OCA\Pantry\Service\HouseAuthService;
use OCA\Pantry\Service\ImageService;
use OCA\Pantry\Service\ShoppingListService;
use OCA\Pantry\Service\ChecklistService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
@@ -27,13 +27,13 @@ use OCP\IUserSession;
* @psalm-import-type PantryListItem from ResponseDefinitions
* @psalm-import-type PantrySuccess from ResponseDefinitions
*/
final class ShoppingListController extends OCSController {
final class ChecklistController extends OCSController {
use TranslatesDomainExceptions;
public function __construct(
string $appName,
IRequest $request,
private ShoppingListService $lists,
private ChecklistService $lists,
private CategoryService $categories,
private HouseAuthService $auth,
private ImageService $images,
@@ -43,7 +43,7 @@ final class ShoppingListController extends OCSController {
}
/**
* List all shopping lists in a house
* List all checklists in a house
*
* @param int $houseId House id.
* @param int<1, 500> $limit Maximum number of lists to return.
@@ -66,7 +66,7 @@ final class ShoppingListController extends OCSController {
}
/**
* Create a shopping list in a house
* Create a checklist in a house
*
* @param int $houseId House id.
* @param string $name List name.
@@ -87,7 +87,7 @@ final class ShoppingListController extends OCSController {
}
/**
* Get a shopping list
* Get a checklist
*
* @param int $houseId House id.
* @param int $listId List id.
@@ -108,7 +108,7 @@ final class ShoppingListController extends OCSController {
}
/**
* Update a shopping list
* Update a checklist
*
* @param int $houseId House id.
* @param int $listId List id.
@@ -143,7 +143,7 @@ final class ShoppingListController extends OCSController {
}
/**
* Delete a shopping list
* Delete a checklist
*
* @param int $houseId House id.
* @param int $listId List id.
@@ -165,7 +165,7 @@ final class ShoppingListController extends OCSController {
}
/**
* List items in a shopping list
* List items in a checklist
*
* Auto-reopens recurring items whose next occurrence has arrived.
*

View File

@@ -23,7 +23,7 @@ use OCP\AppFramework\Db\Entity;
* @method int getUpdatedAt()
* @method void setUpdatedAt(int $updatedAt)
*/
class ShoppingList extends Entity implements \JsonSerializable {
class Checklist extends Entity implements \JsonSerializable {
protected int $houseId = 0;
protected string $name = '';
protected ?string $description = null;

View File

@@ -39,7 +39,7 @@ use OCP\AppFramework\Db\Entity;
* @method int getUpdatedAt()
* @method void setUpdatedAt(int $updatedAt)
*/
class ShoppingListItem extends Entity implements \JsonSerializable {
class ChecklistItem extends Entity implements \JsonSerializable {
protected int $listId = 0;
protected string $name = '';
protected ?int $categoryId = null;

View File

@@ -14,15 +14,15 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
* @template-extends QBMapper<ShoppingListItem>
* @template-extends QBMapper<ChecklistItem>
*/
class ShoppingListItemMapper extends QBMapper {
class ChecklistItemMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, Application::tableName('list_items'), ShoppingListItem::class);
parent::__construct($db, Application::tableName('list_items'), ChecklistItem::class);
}
/**
* @return ShoppingListItem[]
* @return ChecklistItem[]
*/
public function findByList(int $listId): array {
$qb = $this->db->getQueryBuilder();
@@ -38,7 +38,7 @@ class ShoppingListItemMapper extends QBMapper {
/**
* @throws DoesNotExistException
*/
public function findById(int $id): ShoppingListItem {
public function findById(int $id): ChecklistItem {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -50,7 +50,7 @@ class ShoppingListItemMapper extends QBMapper {
/**
* Find all bought items whose next_due_at has passed.
*
* @return ShoppingListItem[]
* @return ChecklistItem[]
*/
public function findDueRecurring(int $now): array {
$qb = $this->db->getQueryBuilder();

View File

@@ -14,15 +14,15 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
* @template-extends QBMapper<ShoppingList>
* @template-extends QBMapper<Checklist>
*/
class ShoppingListMapper extends QBMapper {
class ChecklistMapper extends QBMapper {
public function __construct(IDBConnection $db) {
parent::__construct($db, Application::tableName('lists'), ShoppingList::class);
parent::__construct($db, Application::tableName('lists'), Checklist::class);
}
/**
* @return ShoppingList[]
* @return Checklist[]
*/
public function findByHouse(int $houseId): array {
$qb = $this->db->getQueryBuilder();
@@ -38,7 +38,7 @@ class ShoppingListMapper extends QBMapper {
/**
* @throws DoesNotExistException
*/
public function findById(int $id): ShoppingList {
public function findById(int $id): Checklist {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())

View File

@@ -15,7 +15,7 @@ use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
/**
* Initial schema for Pantry: houses, members, shopping lists, list items.
* Initial schema for Pantry: houses, members, checklists, list items.
*/
class Version1Date20260405000000 extends SimpleMigrationStep {
/**

View File

@@ -7,17 +7,17 @@ declare(strict_types=1);
namespace OCA\Pantry\Service;
use OCA\Pantry\Db\ShoppingList;
use OCA\Pantry\Db\ShoppingListItem;
use OCA\Pantry\Db\ShoppingListItemMapper;
use OCA\Pantry\Db\ShoppingListMapper;
use OCA\Pantry\Db\Checklist;
use OCA\Pantry\Db\ChecklistItem;
use OCA\Pantry\Db\ChecklistItemMapper;
use OCA\Pantry\Db\ChecklistMapper;
use OCA\Pantry\Exception\NotFoundException;
use OCP\AppFramework\Db\DoesNotExistException;
class ShoppingListService {
class ChecklistService {
public function __construct(
private ShoppingListMapper $listMapper,
private ShoppingListItemMapper $itemMapper,
private ChecklistMapper $listMapper,
private ChecklistItemMapper $itemMapper,
private RecurrenceService $recurrence,
) {
}
@@ -25,13 +25,13 @@ class ShoppingListService {
// ----- Lists -----
/**
* @return ShoppingList[]
* @return Checklist[]
*/
public function listForHouse(int $houseId): array {
return $this->listMapper->findByHouse($houseId);
}
public function getList(int $listId): ShoppingList {
public function getList(int $listId): Checklist {
try {
return $this->listMapper->findById($listId);
} catch (DoesNotExistException) {
@@ -39,25 +39,25 @@ class ShoppingListService {
}
}
public function createList(int $houseId, string $name, ?string $description): ShoppingList {
public function createList(int $houseId, string $name, ?string $description): Checklist {
$name = trim($name);
if ($name === '') {
throw new \InvalidArgumentException('List name cannot be empty');
}
$now = time();
$list = new ShoppingList();
$list = new Checklist();
$list->setHouseId($houseId);
$list->setName($name);
$list->setDescription($description !== null && $description !== '' ? $description : null);
$list->setSortOrder(0);
$list->setCreatedAt($now);
$list->setUpdatedAt($now);
/** @var ShoppingList $saved */
/** @var Checklist $saved */
$saved = $this->listMapper->insert($list);
return $saved;
}
public function updateList(int $listId, array $patch): ShoppingList {
public function updateList(int $listId, array $patch): Checklist {
$list = $this->getList($listId);
if (isset($patch['name'])) {
$name = trim((string)$patch['name']);
@@ -89,7 +89,7 @@ class ShoppingListService {
/**
* List items for a list, auto-unchecking any recurring items whose next_due_at has passed.
*
* @return ShoppingListItem[]
* @return ChecklistItem[]
*/
public function listItems(int $listId, ?int $now = null): array {
// Eagerly reopen any due recurring items in this list before returning.
@@ -97,7 +97,7 @@ class ShoppingListService {
return $this->itemMapper->findByList($listId);
}
public function getItem(int $itemId): ShoppingListItem {
public function getItem(int $itemId): ChecklistItem {
try {
return $this->itemMapper->findById($itemId);
} catch (DoesNotExistException) {
@@ -105,7 +105,7 @@ class ShoppingListService {
}
}
public function addItem(int $listId, array $data): ShoppingListItem {
public function addItem(int $listId, array $data): ChecklistItem {
// Ensure the list exists.
$this->getList($listId);
@@ -122,7 +122,7 @@ class ShoppingListService {
}
$now = time();
$item = new ShoppingListItem();
$item = new ChecklistItem();
$item->setListId($listId);
$item->setName($name);
$item->setCategoryId($this->intOrNull($data['categoryId'] ?? null));
@@ -137,12 +137,12 @@ class ShoppingListService {
$item->setSortOrder(isset($data['sortOrder']) ? (int)$data['sortOrder'] : 0);
$item->setCreatedAt($now);
$item->setUpdatedAt($now);
/** @var ShoppingListItem $saved */
/** @var ChecklistItem $saved */
$saved = $this->itemMapper->insert($item);
return $saved;
}
public function updateItem(int $itemId, array $patch): ShoppingListItem {
public function updateItem(int $itemId, array $patch): ChecklistItem {
$item = $this->getItem($itemId);
if (isset($patch['name'])) {
@@ -192,7 +192,7 @@ class ShoppingListService {
return $item;
}
public function toggleItem(int $itemId, string $uid, ?int $now = null): ShoppingListItem {
public function toggleItem(int $itemId, string $uid, ?int $now = null): ChecklistItem {
$item = $this->getItem($itemId);
$now ??= time();
@@ -221,7 +221,7 @@ class ShoppingListService {
* - "fixed schedule" mode: the schedule is anchored at the item's creation time; next
* occurrence is the first one strictly after now on that anchored series.
*/
private function computeNextDueAt(ShoppingListItem $item, int $now): ?\DateTimeImmutable {
private function computeNextDueAt(ChecklistItem $item, int $now): ?\DateTimeImmutable {
$rrule = $item->getRrule();
if ($rrule === null) {
return null;

View File

@@ -8,6 +8,8 @@ declare(strict_types=1);
namespace OCA\Pantry\Service;
use OCA\Pantry\Db\CategoryMapper;
use OCA\Pantry\Db\ChecklistItemMapper;
use OCA\Pantry\Db\ChecklistMapper;
use OCA\Pantry\Db\House;
use OCA\Pantry\Db\HouseMapper;
use OCA\Pantry\Db\HouseMember;
@@ -15,8 +17,6 @@ use OCA\Pantry\Db\HouseMemberMapper;
use OCA\Pantry\Db\NoteMapper;
use OCA\Pantry\Db\PhotoFolderMapper;
use OCA\Pantry\Db\PhotoMapper;
use OCA\Pantry\Db\ShoppingListItemMapper;
use OCA\Pantry\Db\ShoppingListMapper;
use OCA\Pantry\Exception\ForbiddenException;
use OCA\Pantry\Exception\NotFoundException;
use OCP\AppFramework\Db\DoesNotExistException;
@@ -27,8 +27,8 @@ class HouseService {
public function __construct(
private HouseMapper $houseMapper,
private HouseMemberMapper $memberMapper,
private ShoppingListMapper $listMapper,
private ShoppingListItemMapper $itemMapper,
private ChecklistMapper $listMapper,
private ChecklistItemMapper $itemMapper,
private CategoryMapper $categoryMapper,
private PhotoMapper $photoMapper,
private PhotoFolderMapper $photoFolderMapper,

View File

@@ -12,7 +12,7 @@ use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
class ImageService {
public const SHOPPING_ITEMS_SUBDIR = 'Shopping list items';
public const CHECKLIST_ITEMS_SUBDIR = 'Checklist items';
public const PHOTO_WALL_SUBDIR = 'Photo wall';
public function __construct(
@@ -29,7 +29,7 @@ class ImageService {
if ($data === '') {
throw new \InvalidArgumentException('Empty file');
}
$folder = $this->resolveShoppingItemsFolder($uid, $houseId);
$folder = $this->resolveChecklistItemsFolder($uid, $houseId);
$filename = $this->uniqueName($folder, $originalName);
try {
$file = $folder->newFile($filename, $data);
@@ -62,9 +62,9 @@ class ImageService {
return $this->getOrCreateSubFolder($base, self::PHOTO_WALL_SUBDIR);
}
private function resolveShoppingItemsFolder(string $uid, int $houseId): Folder {
private function resolveChecklistItemsFolder(string $uid, int $houseId): Folder {
$base = $this->resolveBaseFolder($uid, $houseId);
return $this->getOrCreateSubFolder($base, self::SHOPPING_ITEMS_SUBDIR);
return $this->getOrCreateSubFolder($base, self::CHECKLIST_ITEMS_SUBDIR);
}
private function resolveBaseFolder(string $uid, int $houseId): Folder {