You say "separate user control logic and formal logic", but you are not able to separate them. For example, OnEnter assumes that when this control get focus, there is at least one row and the first column is not ReadOnly.
I recommend pasting the DataGridView into your UserControl, designing it, and using it. Then you will be able to write code that depends on the design.

Also, if you want to access the form's controls, pass them as properties.
The following example is a delete button.
using System;
using System.ComponentModel;
using System.Windows.Forms;
public partial class Sale_Purchase_DataGridView : UserControl
{
public Sale_Purchase_DataGridView() {
InitializeComponent();
}
[Browsable(false)]
public DataGridView DataGridView { get => dataGridView; }
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
// Your previous question is also answered here.
// or You can do it by design.
dataGridView.Columns[5].ReadOnly = true; // 4?
var column = dataGridView.Columns.GetLastColumn(
DataGridViewElementStates.Visible,
DataGridViewElementStates.None);
column.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
}
protected override void OnEnter(EventArgs e) {
BeginInvoke((Action)(() => {
dataGridView.Focus();
dataGridView.CurrentCell = dataGridView[0, 0];
dataGridView.BeginEdit(true);
}));
base.OnEnter(e);
}
private Button deleteButton;
[Category("Buttons")]
public Button DeleteButton {
get {
return deleteButton;
}
set {
if (deleteButton != null) {
deleteButton.Click -= DeleteButton_Click;
}
deleteButton = value;
if (deleteButton != null) {
deleteButton.Click += DeleteButton_Click;
}
}
}
private void DeleteButton_Click(object sender, EventArgs e) {
if (CanDelete) {
dataGridView.Rows.Remove(dataGridView.CurrentRow);
}
}
protected virtual bool CanDelete {
get {
var current = dataGridView.CurrentRow;
return current != null && !current.IsNewRow;
}
}
}